0

I have two background workers in my application that fill up a table and send log messages. They transmit these data using the "userState" of the ReportProgress method.
They are connected to the main thread through:

msg_worker.DoWork              += message_worker_task;
msg_worker.ProgressChanged     += msg_worker_ProgressChanged;
msg_worker.RunWorkerCompleted  += worker_completed;

data_worker.DoWork             += data_worker_task;
data_worker.ProgressChanged    += data_worker_ProgressChanged;
data_worker.RunWorkerCompleted += worker_completed;

They are synchronized using two EventWaitHandle items that they set at the end of their doWork task:

private void message_worker_task(object sender, DoWorkEventArgs e)
    {
        try
        {
            while( .. ){ 
               // do work
               bw.ReportProgress( 10, new String("my message to append"));
            }
        }
        finally
        {
            e.Result = 0;
            msg_done.Set(); // EventWaitHandle
        }
    }

The Progress changed delegates insert data/log in the components that are visible to the user.

Now, the problem is that when I click on the Quit button in my UI, I wait on those 2 threads like this:

private void wait_threads()
    {
        int timeout = 30000;
        Cursor.Current = Cursors.WaitCursor;
        if (data_worker.IsBusy && !data_worker.CancellationPending)
        {
            data_worker.CancelAsync();
            data_done.WaitOne(timeout);
        }
        if (msg_worker.IsBusy && !msg_worker.CancellationPending)
        {
            msg_worker.CancelAsync();
            msg_done.WaitOne(timeout);
        }
        Cursor.Current = Cursors.Default;
    }

This seems to be working (I use it at other places in the code), but in this "quitting" case, I have an exception raised, saying that I tried to insert data into the log component I have. This component is manipulated by the RunWorkerCompleted delegate.

The code which gets executed when Quit is clicked is this:

    private void quit_Click(object sender, EventArgs e)
    {
        wait_threads(); // blocks until threads are finished
        Close();
    }

I think I figured out that while waiting for the threads to complete, they send a last event: the RunWorkerCompleted. The problem is that I'm in the process of Close() 'ing my form, which means the form is not in a correct state. What's more, if I check the e.Cancelled flag in the worker_completed function, I see it as false...

What can I do to make sure the RunWorkerCompleted is handled before my Close() function gets executed? (or rather, is not handled at all, because we're quitting).

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Gui13
  • 12,993
  • 17
  • 57
  • 104

1 Answers1

0

I think you can check the property Cancellation Pending before calling ReportProgress.

while( .. ){ 
    if (!bw.CancellationPending) 
              bw.ReportProgress( 10, new String("my message to append"));
}
misleadingTitle
  • 657
  • 6
  • 21
  • Actually, I might have been wrong. I figured that's the RunWorkerCompleted that is raising the exception. In the code I see that I use the components as well. – Gui13 Apr 17 '13 at 11:18
  • What if you put a `WaitOne` before the `Close()` on a `WaitHandle` that is setted in the `DataWorkerComplete`? – misleadingTitle Apr 17 '13 at 12:11