Browse DevX
Sign up for e-mail newsletters from DevX


Asynchronous Windows Forms Programming : Page 4

Learn how to implement BackgroundWorker in Visual Studio .NET 2003 so you can benefit from its elegant programming model and transition transparently into Windows Forms 2.0 in the future.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

Implementing BackgroundWorker with .NET 1.1
What disturbs me about Listing 3 is the disparity between it and Listing 1. Windows Forms Developers today are stuck with an awkward, fragile programming model, and have to wait (probably) until early 2005 to benefit from BackgroundWorker. Most Windows Forms applications that use asynchronous calls today will not be able to use BackgroundWorker easily because of the high degree of internal coupling. To address this predicament, I implemented BackgroundWorker using .NET 1.1, and it is available with the source code for this article. Listing 4 shows my implementation of BackgroundWorker, defined in the WinFormsEx namespace. The implementation uses some advanced .NET programming techniques and tricks, and makes for an interesting walkthrough.

Your code that uses BackgroundWorker today will transparently be able to use BackgroundWorker in .NET 2.0.
The first step in implementing BackgroundWorker was to define and implement the supporting delegates and event arguments classes shown in Listing 2—a simple matter of implementing properties. Implementing RunWorkerAsync() was simple too: first, it checks that there is a target method (when DoWork is not null) to invoke. RunWorkerAsync() then constructs a new DoWorkEventArgs object with the method argument and invokes DoWork using BeginInvoke() to dispatch the call asynchronously:

DoWorkEventArgs args = new DoWorkEventArgs(argument); AsyncCallback callback; callback = new AsyncCallback(ReportCompletion); DoWork.BeginInvoke(this,args,callback,args);

Using a delegate of type AsyncCallback, RunWorkerAsync() designates the private helper method named ReportCompletion() as the completion callback method. .NET will call ReportCompletion() once the asynchronous method execution completes.

Note that RunWorkerAsync() passes the DoWorkEventArgs argument twice to BeginInvoke(). RunWorkerAsync() uses the first DoWorkEventArgs as the parameter for the target method of DoWork(). It uses the second DoWorkEventArgs as an additional parameter to ReportCompletion(). Because both args reference the same object, changes made to args inside the asynchronous method (such as cancellation or result setting) will be available to ReportCompletion() as well. When you call ReportCompletion(), .NET provides it with a parameter of type IAsyncResult. You down cast it to AsyncResult and use it to obtain the original delegate used to invoke the call:

AsyncResult ar = (AsyncResult)asyncResult; DoWorkEventHandler del; del = (DoWorkEventHandler)ar.AsyncDelegate;

You need the original delegate so that you could call EndInvoke() and end the call. Note that you cannot simply access the DoWork delegate due to a potential race condition. If the asynchronous method was executing, something could remove the target method from DoWork. In that case, not only would you not be able to call EndInvoke(), you will also trigger a resource leak. AsyncResult contains a copy of the original delegate and its state at the time of invocation.

Any exception thrown by the asynchronous method will be caught by the worker thread, and it will be re-thrown when you call EndInvoke(). ReportCompletion() catches such an exception and saves it in a temporary variable. In addition, ReportCompletion() saves the result of the asynchronous method made available through the DoWorkEventArgs passed in as a state object to BeginInvoke(), and set inside the asynchronous method:

object result = null; Exception error = null; try { del.EndInvoke(asyncResult); result = doWorkArgs.Result; } catch(Exception exception) { error = exception; }

ReportCompletion() then needs to notify the subscribers of the RunWorkerCompleted delegate about the method completion. It constructs an object of the type RunWorkerCompletedEventArgs and populates it with the result, error, and cancellation information.

However, ReportCompletion() is called on the worker thread, therefore it cannot invoke the RunWorkerCompleted delegate directly. Instead, it calls the protected virtual method OnRunWorkerCompleted(), which calls the private ProcessDelegate() helper method defined as:

void ProcessDelegate(Delegate del,params object[] args);

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date