Asynchronous Winforms Programming
by Jon Wojtowicz

Winforms programs often need to make call to long running operations. These can be calculations, data access calls, or calling web services. It's important to allow the user to interact with the application. You may also want to display the progress of the operation to the user.


Long Running Operation
For this demonstration I'll be using the following method to simulate a long running operation.
protected void LongRunningMethod()
{
int count = 10000;
int step = 100;
while (count > 0)
{
System.Threading.Thread.Sleep(step);
count -= step;
}
}
As you can see, this operation will pause for about 10 sec while the thread sleep operations are carried out.
So what happens if we call this method synchronously? The application will appear to the user to have locked up. If the window is covered and then uncovered it will also not repaint. To demonstrate this to yourself you can include a call to this method from any event handler on your form and check out the results.
Asynchronous Operations
The .Net framework provides a design pattern for asynchronous calls. This allows them to be called in a uniform manner across different parts of the framework. Many .Net class have built-in support for asynchronous programming such as I/O, sockets, messaging and web services. The .Net framework makes the details of making the asynchronous calls nearly transparent to the developer.
The .Net framework also provides each application with a pool of 20 worker threads. This pool of background threads is created when the application starts. The asynchronous pattern uses these threads to complete the asynchronous calls. This saves the developer from having to write the code to manage the threads.
To make an asynchronous call a call to the BeginOperation is called. The caller also needs to decide on the completion notification technique to use. The notification techniques available are:
  • Using a callback delegate. The delegate will be invoked when the operation is completed.
  • Poll to check for completion. The IAsyncResult interface's IsCompleted property can be checked.
  • Call the EndOperation. This will cause the current thread to be blocked until the operation is complete.
  • Wait on a handle. Wait on the IAsynchResult interface's WaitHandle property. Then the EndOperation can be called.
It's important to note that when using the BeginOperation that the EndOperation must be called to prevent potential memory leak issues.
Asynchronous Callback
When using the asynchronous callback there are four main steps in the process.
  1. Create the asynchronous delegate to point back to the methods we want to call when the operation completes. The AsyncCallbackDelegate has the following signature void AsyncCallback( IAsyncresult ar)
  2. Initiate the asynchronous call by using the BeginOperation passing it the async delegate.
  3. Inside the callback call the appropriate EndOperation method to retrieve the results.
  4. Return control to the main thread to update the user interface.
Item 4 is very important. The UI can only be safely updated from the UI thread. The UI should not be updated by any other thread.
The async callback can be created from any method that matches the delegate. The following method can be used to create our callback.
public void CallBackDel(IAsyncResult ar)
{
}
To create the async callback we use the following.
AsyncCallback ac = new AsyncCallback(CallBackDel);
The async callback is then passed to the begin method of our delegate.
MethodInvoker mi = new MethodInvoker(LongRunningMethod);
mi.BeginInvoke(ac, null);
In order to retrieve the results on the completion notification we change the callback method to the following. Since we do not have any return data we do not have to perform any assignment. If the async call generates any exceptions, the call to EndInvoke will cause them to be raised.
protected void CallBackDel(IAsyncResult ar)
{
mi.EndInvoke(ar);
}
Since this method takes no arguments and doesn't return a value it can also be executed on a foreground thread using a ThreadStart delegate.
System.Threading.ThreadStart ts = new System.Threading.ThreadStart(LongRunningMethod);
System.Threading.Thread t = new System.Threading.Thread(ts);
t.Start();
The last part is returning control to the main UI thread to update the UI. This requires another delegate referencing an UI updater method. The BeginInvoke method of the form is used to place the delegate into the event pump to get called.
protected void UpdateUI()
{
}

MethodInvoker updaterMI = new MethodInvoker(UpdateUI);
this.BeginInvoke(updaterMI);
Using this code the LongRunningMethod becomes the following.
protected void LongRunningMethod()
{
int sleepTime = 10000;
int currentTime = sleepTime;
int step = 100;
while (currentTime > 0)
{
System.Threading.Thread.Sleep(step);
currentTime -= step;
}
MethodInvoker updaterMI = new MethodInvoker(UpdateUI);
this.BeginInvoke(updaterMI);
}
The BeginInvoke is used as it releases the worker thread immediately.
Protecting Data in a Mutithreaded Environment
When in a multithreaded environment data must be protected from multiple threads modifying the data simultaneously. While not truly simultaneous, one thread could be in the middle of a modification when it gets interrupted and another thread starts. This is called a race condition because the first one wins.
.Net provides several options for synchronizing or protecting data. Only using the Monitor class will be discussed. The Monitor is used like the following where the item in the parenthesis is the item to lock.
lock(this)
{
//code to access the data goes here
}
This statement causes the compiler to emit code to use the Monitor class to protect the data. This will allow only a single thread into the protected section at any time. It is important to design applications that minimize synchronization needs.
Status Bar
The status bar is a common UI feature to inform the user about long running processes. Since the UI should only be updated by the UI thread the async thread needs a means of notifying the UI on its progress. Events would appear to be the logical choice for communicating the status. In order to safely pass data back and for the between threads a reference type should be used. Since this is an event a new class derived from EventArgs should be used.
public class NotificationEventArgs : EventArgs
{
private int percentComplete;
public int PercentComplete
{
get{ return percentComplete;}
set{ percentComplete = value;}
}
public NotificationEventArgs(int percentComplete)
{
this.percentComplete = percentComplete;
}
}
The event handler delegate and method need to be created.
public delegate void UpdateStatusDelegate( object sender, NotificationEventArgs e);

public void UpdateStatusHandler( object sender, NotificationEventArgs e)
{
progressBar1.Value = e.PercentComplete;
}
The long running method can be changed to allow for this update to occur.
protected void LongRunningMethodUI()
{
int sleepTime = 10000;
int currentTime = sleepTime;
int step = 100;
UpdateStatusDelegate updateStatus = new UpdateStatusDelegate(UpdateStatusHandler);
NotificationEventArgs e = new NotificationEventArgs(0);
Object sender = System.Threading.Thread.CurrentThread;
updateStatus(sender, e);
while (currentTime > 0)
{
System.Threading.Thread.Sleep(step);
currentTime -= step;
e.PercentComplete = 100 - (int)(((double)currentTime / (double)sleepTime)* 100);
updateStatus(sender, e);
}
}
This solution still updates the UI on the background thread. A solution is needed to get the event to run on the UI thread. The Control class contains a solution to this issue. The InvokeRequired determines if the call is coming from the current UI thread or from a separate thread. The update method can be changed to incorporate this in the following manner.
protected void UpdateStatusHandler ( object sender, NotificationEventArs e)
{
if( this.InvokeRequired)
{
UpdateStatusDelegate updateStatus = new UpdateStatusDelegate(UpdateStatusHandler);
this.Invoke(updateStatus, new object[]{sender, e});
}
else
{
progressBar1.Value = e.PercentComplete;
}
}
This final variation will cause the event to be placed on the UI thread for completion.
Summary

Creating multithreaded applications in .Net has been made fairly simple. Some care must be made in determining how to best leverage this capability. Hopefully this brief summary will provide a brief background for further investigation.

Download the code that accompanies this article

Jon Wojtowicz is a C# MVP and a Systems Analyst at a large insurance company in Chattanooga, TN where he currently provides developer support and internal training. He has worked as a consultant working with Microsoft Technologies. This includes ASP, COM, VB6 and .Net, both C# and VB.Net since Beta 1. He has been an MCSD since 1999 and an MCT since 2000. Prior to getting a degree in Computer science he worked as a process engineer focusing on process automation, programmable controllers and equipment installations. In his spare time he likes woodworking and gardening.