Trouble understanding threading

I've got an application written in C# 2.0, and am trying to speed up my reporting a bit. On the main form, I have a button that will create a report that can take quite some time. While this report is being created, my main form goes unresponsive. The button doesn't even pop back up while I'm waiting on it. I figured I'd spin off a thread and have it create the report. It worked, but the second the form is loaded, it goes away. How can I get this form to load separate from my main thread, and then somehow come back so it doesn't go away


Answer this question

Trouble understanding threading

  • scott00

    I tried using an event that fired at the end of my thread, and I ended up having the same problem where the form would load, but would then disappear as soon as the thread that invoked the handler was finished. I've got it working using a timer on the form that checks to see if the thread is finished every half second. This is some of the code that I've got that didn't work. Maybe you can spot an error or omission that would make it work with the event rather than the timer.


    event EventHandler ReportCreated;

    public ReportCreator(PhonePageParameters phonePageParameters)
    {
    InitializeComponent();
    Text = Application.ProductName;

    ThreadStart threadStart = delegate
    {
    List<PhonePage> phonePageList = PhonePageManager.Read(phonePageParameters.sxs, phonePageParameters.reportType);

    PhonePageXtraReport xtra = new PhonePageXtraReport(phonePageList);

    Thread.Sleep(0);
    xtra.CreateDocument();
    Thread.Sleep(0);
    report = xtra;

    if (ReportCreated != null)
    { ReportCreated.Invoke(this, EventArgs.Empty); }
    };

    thread = new Thread(threadStart);

    ReportCreated += new EventHandler(ReportCreator_ReportCreated);
    }

    private void ReportCreatorForm_Load(object sender, System.EventArgs e)
    {
    if (thread != null)
    {
    thread.Start();
    }
    }

    void ReportCreator_ReportCreated(object sender, EventArgs e)
    {
    Close();
    if (Report != null)
    Report.ShowPreview();
    }

  • helpPlease176795

    First I would like to point out that you probably should use the BackgroundWorker if you're doing C# 2.0. But if you insist on using the code you provided I think it would be better if you removed the "Close();" in the eventhandler as the form you're closing is holding the reference to the Report (I guess).


  • raceirolg

    By far the easiest way for you to do this is to drop a BackgroundWorker object onto the form from the Form Designer. (BackgroundWorker is somewhere in the Toolbox.)

    Then do your work in the RunWorkerAsync() method.

    See here for details: http://msdn2.microsoft.com/en-US/library/4852et58.aspx

    The key thing is you do the work in a different thread from the user interface thread. The user interface thread just sits around waiting for the BackgroundWorker's "RunWorkerCompleted" event to fire.

    I commonly have a boolean variable in the form that I set to true in response to the RunWorkerCompleted event. I inspect that variable to prevent the user from doing anything they shouldn't until the work is done (e.g. when handing FormClosing, I can prevent the form from closing).



  • Bonnie Colleen

    I went through and was able to make use of the BackgroundWorker to create my report, and it properly shows the report without disappearing when the thread ends or the form is closed. The problem I'm having now is with the cancel method behind it. Previously, I was creating a thread, and having it run through until completion, and if the cancel button was pressed, I would just kill the thread. With the BackgroundWorker, the only thing you can do is call CancelAsync();, which tells the thread that a cancel was requested. This works if you have something looping through over and over again, or a bunch of small commands, but I'm running one line of code that can take quite some time, and I don't have any control over what is going on within that code. If I hit cancel, it will cancel, but only after you've waited the full time it would take to generate the report. Maybe killing the thread is bad, and shouldn't happen, but I can't see any alternative in this case.

  • MrMoke

    You're opening the 2nd form from your working thread I think you shouldn't do that. Because, your thread is now the owner of the form, and once the thread is finished, it also destroys the form.

    I guess that you should create a class, that has a run method, and when this run method is called, your 'task' (creating the report) should be done on a separate thread. Then, when this is finished, your class should raise an event, and you can open the 2nd form from this event-handler.

    For instance:

    public class CreateReportTask
    {

    public event EventHandler ReportCreated;

    public void Run()
    {
    Thread t = new Thread (new ThreadStart(DoWork));
    t.Start();
    }

    private void DoWork()
    {
    // Create the report

    // When finished, raise the event.
    }

    private void OnTaskFinished()
    {
    if( ReportCreated != null )
    {
    ISynchronizeInvoke s = ReportCreated.Target as ISynchronizeInvoke;

    if( s != null && s.InvokeRequierd )
    {
    s.Invoke (ReportCreated, new object[] {this, EventArgs.Empty});
    }
    else
    {
    ReportCreated (this, EventArgs.Empty);
    }
    }
    }
    }


    Before you launch the task, you can assigne an eventhandler to the event, and this event-handler can show your 2nd form.
    (You can offcourse use a custom delegate instead of EventHandler, so that you can create and pass your own event-arguments).

    You might also have a look at the BackgroundWorker object in .NET 2.0, since this has almost the same behaviour as my example. But, since I'm still mainly using .NET 1.1, I always create my own Task pattern.


  • Trouble understanding threading