.NET 2.0 BackgroundWorker
The Windows Forms team and the .NET architects are well-aware of the problems just described. To address them, the next version of .NET (Whidbey) contains a new component called BackgroundWorker defined in the System.ComponentModel namespace. If you have access to the Whidbey beta distributed at PDC, you can find BackgroundWorker in the Components tab of a Windows Forms project. If you drop it on a form, you can use BackgroundWorker to dispatch asynchronous work, report progress and completion, and do all that while encapsulating the interaction with ISynchronizeInvoke. Using this approach gives developers a much smoother and superior programming model. Listing 2
shows the definition of BackgroundWorker and its supporting classes.
|Use BackgroundWorker to dispatch asynchronous work, report progress and completion, and do all that while encapsulating the interaction with ISynchronizeInvoke.|
BackgroundWorker has a public delegate called DoWork of the type DoWorkEventHandler. To invoke a method asynchronously, wrap a method with a matching signature to DoWorkEventHandler and add it as a target to DoWork. Then, call the RunWorkerAsync()
method to invoke the method on a thread from the thread pool:
backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += OnDoWork;
void OnDoWork(object sender,DoWorkEventArgs doWorkArgs)
BackgroundWorker offers two overloaded versions of RunWorkerAsync()
public void RunWorkerAsync();
public void RunWorkerAsync(object argument);
You can use the argument parameter to pass any argument to the asynchronous method. Internally, RunWorkerAsync()
will construct a DoWorkEventArgs object containing the argument, invoke the DoWork delegate asynchronously, and pass it the DoWorkEventArgs
object and itself as the sender.
The asynchronous method can access the Argument
property of DoWorkEventArgs
to retrieve the argument passed to RunWorkerAsync()
. The asynchronous method should also set the value of the Result
property of DoWorkEventArgs with the retuned value.
Any party interested in being notified when the asynchronous method is completed should subscribe to the RunWorkerCompleted
member delegate of BackgroundWorker. RunWorkerCompleted
is a delegate of the type RunWorkerCompletedEventHandler. The completion notification method accepts a parameter of type RunWorkerCompletedEventArgs, which contains the result of the method execution (the value you set in the Result
property of DoWorkEventArgs
inside the asynchronous method), as well as error and cancellation information.
When the asynchronous method execution is completed, BackgroundWorker
cannot simply invoke the RunWorkerCompleted
delegate because that invocation will be on the thread from the thread pool, and any control or form that subscribed to RunWorkerCompleted cannot be called directly. Instead, BackgroundWorker checks whether each of the target objects in RunWorkerCompleted supports ISynchronizeInvoke, and if invoke is required. If so, it will marshal the call to the owning thread of the target object.
To support progress reports, BackgroundWorker
provides a member delegate called ProgressChanged
of the type ProgressChangedEventHandler. Any party interested in progress notification should subscribe to ProgressChanged
. When the asynchronous method wishes to notify about progress, it calls BackgroundWorker's method, ReportProgress
, to correctly marshal the progress notification to any Windows Forms object.
To cancel a method, anybody from any thread can call BackgroundWorker's CancelAsync()
method. Calling CancelAsync()
results in having the CancellationPending
property of BackgroundWorker return true
. Inside the asynchronous method, you should periodically check the value of CancellationPending
, and if it is true
, you should set the Cancel
property of DoWorkEventArgs
and return from the method. In the completion method you could check the value of the Cancelled
property of RunWorkerCompletedEventArgs
to detect whether the method has run to its completion or it was cancelled.
In case you derive from BackgroundWorker in order to specialize its behavior, the subclass you provide may also want to be notified of progress reports and completion events. One way to do that would be to provide event handling methods at the scope of the subclass, and add these methods as targets for the ProgressChanged
member delegates. However, it is a bit inconvenient to have to subscribe to events of your own base class. To that end, BackgroundWorker provides two protected virtual methods: OnProgressChanged()
. These methods invoke their respective delegates, ProgressChanged
. You can override them, and perform some pre or post event processing:
public class MyBackgroundWorker : BackgroundWorker
protected override void OnRunWorkerCompleted(
// do some pre-event processing, then:
Note that it is very important to call BackgroundWorker's implementation of these methods; otherwise the events will never be raised.
has two Boolean properties that control progress reports and cancellationWorkerReportsProgress
. Both properties are false
by default, but of course you need to set them to true
in most cases.
shows the same application as in Listing 1
, except this time it uses a single BackgroundWorker component. Note how smooth and easy the code is for handling progress reports and completion. Because you are guaranteed to always execute on the correct thread, you can access and assigned the controls' properties directly.