BackgroundWorker class

B

Bob Chambers

Hi there,

Can anyone comment on two apparent problems I've noticed with the
"BackgroundWorker" class (e.g., the design pattern itself). The canonical
examples always show a demonstration of a "Cancel" button handler invoking
"BackgroundWorker.CancelAsync()" but two possible problems then arise:

1) What happens if the "Cancel" button handler is running around the same
time that the worker thread is exiting. In this case, the worker thread may
exit in which case the "RunWorkerCompleted()" handler will be invoked as
soon as the "Cancel" handler returns. The "RunWorkerCompletedEventArgs"
argument passed to "RunWorkerCompleted()" will then indicate that the thread
exited either normally or via an exception but not by cancellation even
though the "Cancel" handler just ran!

2) If any dialogs are displayed on the main UI thread while the background
thread is running (say, merely by asking the user to confirm cancellation
when they click the "Cancel" button), then because a dialog pumps messages,
if the background thread ends while a dialog is still on screen then
"RunWorkerCompleted()" will get called prematurely. IOW, using the example
just cited, the confirm "Cancel" dialog may still be on screen at this point
but "RunWorkerCompleted" is now called indicating the thread ended normally
or via an exception which won't be true if the user then confirms
cancellation (moreover, after confirming cancellation, the "Cancel" handler
will then try to cancel the background thread which has already finished and
"RunWorkerCompleted()" won't be called again).

Can anyone comment on these issues. Thanks.
 
B

Bob Chambers

What would you have it do? as in, how would you make it better?

Well, there may not be a "one solution fits all" approach but at the very
least I wouldn't post broken examples. For instance, all
"RunWorkerCompleted()" examples I've seen so far basically look like this:

private void RunWorkerCompleted(RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
// User cancelled and thread responded
}
else if (e.Error != null)
{
// Thread ended via an exceptin
}
else
{
// Thread ended normally
}
}

The test for "e.Cancelled" is ok but the other two (error or normal exit)
fail to consider that the
"System.Component.BackgroundWorker.CancellationPending" flag may still be
"true" indicating the the user cancelled out but it was too late for the
thread to respond. At first glance, the function appears as if it should be
changed to this instead (or something similar):

private void RunWorkerCompleted(RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
// User cancelled and thread responded
}
else if (!m_BackgroundWorker.CancellationPending)
{
if (e.Error != null)
{
// Thread ended via an exception
}
else
{
// Thread ended normally
}
}
else
{
// User cancelled but it was too late for the thread to respond
}
}

The final "else" clause above will be problematic anyway if the programmer
is now forced to roll back any changes that the worker thread has
effictively completed. My first reaction to all this anyway is that the
entire model should be enhanced so that the worker thread simply needs to
(optionally) inform the main GUI thread that it's reached a point where
cancellation will no longer be an option (normally at or near the end of its
work). The worker thread will then pause while the main GUI thread will
typically respond by disabling its "Cancel" button. The worker thread then
receives notification (via return code) that the "Cancel" button was either
disabled as requested (the worker thread can then wrap up what it's doing),
or notification that the user already pressed the "Cancel" button during
this brief turn-around time. In this case the worker thread will cancel as
usual. The first "RunWorkerCompleted()" function above will then work as
expected. In any case, while I haven't considered all the details yet, more
coordination between the main GUI thread and the worker thread is required
to handle this situation in general. And this doesn't not even consider the
second issue I reaised in my first post which complicates matters further.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top