Windows Forms Threading

(by Joe Williams for Blayd Software)

Note: this article relates to the .Net Framework version 2.0, some of the classes and class members referenced in this article were not available in earlier framework versions.

Introduction

Windows Forms applications are designed and developed to interact with a user, the interaction may take many forms and may be for many different reasons. For example, an application may display data in a useful and informative way, capture and validate data input from a user, provide access to functions or services that process data or provide all of these features and possibly more. Designing an intuitive and effective user interface for a Windows Forms application is a complex and skilled process and there are many objectives that have to be fulfilled. One of the key objectives is to provide a responsive application, users generally don't like waiting around while an application is retrieving data or performing calculations, for example. An application that requires user interaction must react to the user's activities as rapidly as possible to provide a rich user experience. At the same time, however, an application often has to perform the calculations and formatting necessary to present data to the user as quickly as possible.

When developing a Windows Forms application we want the application to be as responsive as possible when interacting with the user, even if the application is currently performing other work. Multithreading (using multiple threads of execution) is one way to keep an application responsive to the user whilst making use of the processor in between or even during user activated events. Using more than one thread is an effective technique to increase the responsiveness of the user interface and process data at the same time or almost the same time on a single processor (single core) machine. On a machine with multiple processors or a multi-core processor, that can run more than one thread concurrently, the improvement in user interface response can be quite dramatic and even on a machine with one (single core) processor, multiple threads can create the same effect by taking advantage of the periods of time between user activated events to process data in the background.

In this article we will be discussing several techniques that can be used to run a task on a background or worker thread in a Windows Forms application. We will also be discussing asynchronous methods and showing how they can be used in classes or components that provide services, either from within the same assembly or from a class library, for a Windows Forms application.

Interacting with the User Interface from a Background Thread

Before we start discussing creating and running background threads and implementing asynchronous methods there is one important aspect of multithreading in Windows Forms applications that has to be appreciated. Controls, including Form, in Windows Forms operate in a single-threaded apartment (STA) because Windows Forms is based on native Win32 widows that are apartment threaded. The STA threading model allows a window to be created on any thread, however, it cannot switch threads once it is created and all function calls to the window must occur on the thread on which it was created. Therefore, in a standard Windows Forms application, all forms and controls, typically, execute on the same thread, the primary or main UI thread on which the application was launched. Each control is bound to the primary thread, which runs its message pump and cannot be updated from a different thread, therefore, controls in Windows Forms are not thread-safe. If you try to update a control, for example, by setting its Text property from a thread other than the main UI thread you may force the control into an inconsistent state. The update may work or it may not, you may get thread-related bugs such as race conditions or deadlocks or in the worst case the application may hang or crash.

If the application is running in the debugger from within Visual Studio 2005 attempted illegal cross thread calls, such as the one described above, will be intercepted by the debugger and an InvalidOperationException will be thrown. The illegal cross thread call exception is documented as follows "This exception occurs reliably during debugging and, under some circumstances, at run time. You are strongly advised to fix this problem when you see it".

Background threads that do not interact with the user interface do not present a problem, however, it is a common requirement for a background task to inform the user of the of the task's progress, by updating a progress bar control, for example, during the execution of the task. Therefore a reliable, thread-safe, way of updating a user interface control from a background thread is required. The Control base class provides three thread-safe methods BeginInvoke, EndInvoke and Invoke that can be used to interact with a user interface control from a background thread, it also provides the InvokeRequired property that can be used to determine whether the caller is on a different thread from the one the control was created on.

The code sample below shows how to update a user interface control from a background thread, the code to create the background thread and sink the event has been omitted for clarity:

ExpandC# user interface control update from a background thread
ExpandVB user interface control update from a background thread

The code sample first creates a private delegate which will be passed to the Control.Invoke method and used to call the UpdateProgress method on the main UI thread. The delegate signature must match the signature of the user interface control update method. The AsyncProgressChanged method represents the ProgressChanged event handler that will be called on a background thread. The handler first checks if the call is on the same thread as the control that is to be updated, in this example we can just use the form's inherited InvokeRequired property as we know that the progress bar control is running on the same, main UI, thread as the form. If the call is on a different thread, the code calls the Control.Invoke method (we can use the Form as we did for InvokeRequired) passing in an instance of the ProgressUpdate delegate and the event data as the only element in an Object array. The Object array represents the arguments of the method that is to be called by the delegate, therefore the UpdateProgress method will be passed the ProgressChangedEventArgs instance when it is called by the delegate. The Control.Invoke method marshals the data to the main UI thread and then invokes the delegate on the main UI thread. Because we know that the UpdateProgress method will always be called on the main UI thread we can go ahead and update the progress bar control.

Threading Options

If your threading requirements are fairly straightforward we strongly recommend that you consider using the System.ComponentModel.BackgroundWorker component rather than explicitly creating and managing your own threads. We will be discussing the BackgroundWorker component in detail later in the article so we won't go into too much detail here. However, when choosing which of the threading options is best for your situation one of the main benefits of the BackgroundWorker component is that it implements the Event Based Asynchronous Design Pattern which can make UI control updates a lot easier.

The BackgroundWorker component has three public events that you should handle in your code:

The DoWork event
This is the event from which you should run your background task. The DoWork event handler is called on a background thread and should only be used to run the background task, you should not update UI controls from within the event handler or from within methods that are called from the event handler.
The ProgressChanged event
You can raise this event from within your background task by calling one of the BackgroundWorker.ReportProgress method overloads. Internally the BackgroundWorker component uses the System.ComponentModel.AsyncOperationManager class to marshal the data and then raise the ProgressChanged event on the appropriate thread. Therefore, if the BackgroundWorker component was created on the main UI thread and you handle the ProgressChanged event on the main UI thread, you can update UI controls directly from within your handler code.
The RunWorkerCompleted event
This event is raised by the BackgroundWorker component when the DoWork handler code returns i.e. when the background task has completed, been cancelled or has encountered an error. Like the ProgressChanged event the RunWorkerCompleted event handler will be called on the main UI thread, if the BackgroundWorker component was created on the main UI thread, therefore you can update UI controls directly from within your handler code.

See the BackgroundWorker Component section for more details on using the BackgroundWorker component.

The .Net Framework provides two options for developers who want to create and/or manage their own background or worker threads in an application. You can use the System.Threading.Thread class to create your own thread or threads or you can use the static System.Threading.ThreadPool class to retrieve a thread or threads from the thread pool.

The ThreadPool class provides an application with a pool of worker threads that are managed by the system. There is one thread pool per process and by default it contains 25 worker threads per processor. Thread pool threads are background threads i.e. their Thread.IsBackground property is set to True, therefore, a thread pool thread will not keep an application running after all foreground threads have exited. For an application that has short tasks that require background processing, the managed thread pool provides an efficient and easy way to utilise multiple threads.

The code sample below shows how to run a background task on a thread from the thread pool:

ExpandC# background task running on a thread from the thread pool
ExpandVB background task running on a thread from the thread pool

The sample code is contained in a Form class and consists of a Button.Click event handler that triggers the background task on a thread from the thread pool and the BackgroundTask method which represents the background task that is to run on a thread from the thread pool. ThreadPool is a static class and therefore all of the code in your application will use the same thread pool. Because your application has access to only one thread pool you should be careful when using the thread pool for really long running tasks or tasks that are likely to block frequently or for long periods, as a thread pool thread cannot be reused until it has finished its work (see the Locking, Blocking, Racing and Deadlocking section). You can set the minimum (idle) and the maximum number of threads on the ThreadPool class, however, you should use caution when changing the minimum number of idle threads and/or the maximum number of threads in the thread pool. Whilst your code might benefit, the changes might have an adverse effect on code libraries used by your application.

A thread pool thread is requested by calling the ThreadPool.QueueUserWorkItem method, passing in a WaitCallback delegate and an Object that represents the argument for the background task method. The WaitCallback delegate represents the method that is to be executed on a thread pool thread. There is a QueueUserWorkerItem method overload that does not require the Object argument, however, it does still require a WaitCallback delegate and the WaitCallback delegate signature requires an Object state parameter, so using this overload is the same as passing a null argument to the background task method. The QueueUserWorkItem method returns True if the delegate is successfully queued or False if not. The delegate will be queued by the ThreadPool class until an idle thread becomes available, therefore, it is not safe to assume that your background task will be called immediately, nor is it safe to assume that your delegate will be the only item in the queue. As soon as a thread becomes available, the ThreadPool class will use it to invoke the delegate and execute the background task method, passing in the argument Object, if one was specified or null if not.

The Thread class provides the maximum flexibility for running a task or tasks on a dedicated thread and is not particularly difficult to use. The flexibility of the Thread class derives from the fact that you can use its properties and methods to setup the thread to match your requirements, however, to do this you really need to know what you are doing. In most situations a thread retrieved from the thread pool or the thread used by an asynchronous delegate (which is a thread pool thread!) is perfectly adequate and you do not have to explicitly set it up or start it.

Some of the situations that may require the use of the Thread class, rather than the ThreadPool class are as follows:

  • You require a foreground, rather than a background thread.
  • You need to set the thread priority.
  • You have a task or tasks that cause the thread to block frequently or for long periods of time. The thread pool has a maximum number of threads, so a large number of blocked thread pool threads might prevent other tasks from starting.
  • You need to have a specified and stable identity associated with the thread or you need to dedicate a specific thread to a specific task.
  • You need to place the thread into a single-threaded apartment (STA). All ThreadPool threads are in a multithreaded apartment.

The code sample below shows how to run a background task using and instance of the Thread class:

ExpandC# background task running on an explicitly created thread
ExpandVB background task running on an explicitly created thread

The sample code is contained in a Form class and consists of a Button.Click event handler that triggers the background task on an explicitly created thread and the BackgroundTask method which represents the background task that is to run on the thread. The ParameterizedThreadStart delegate is used to allow a state or argument Object to be passed to the Thread.Start(Object) method. If a state or argument Object is not needed, you can use the ThreadStart delegate and then call the parameterless Thread.Start method overload. If the parameterless overload of the Start method is used and the thread was created using a ParameterizedThreadStart delegate a null reference will be passed to the method executed by the thread. When using the Thread class we can setup the thread before starting it, in the sample code we have specified that we require a background thread by setting the Thread.IsBackground property to True. A background thread does not prevent a process from terminating. When all foreground threads belonging to a process have terminated, the common language runtime ends the process. Any remaining background threads are stopped and do not complete. This can be a problem in some situations, so we have added a call to the Thread.Join method to demonstrate how to force the calling (main UI) thread to wait for the background thread to finish. This does, at first, seem to be counter productive, however if you are trying to cancel a background task because the application has been closed by the user, this technique allows the background thread to close cleanly, providing that you can request it to stop or cancel, rather than aborting. The sample also includes a commented out call to the Thread.Sleep method, which you may need to reinstate if you are running the sample on a single processor, single core, machine. This call makes the calling (main UI) thread yield so that the background thread can run. Finally we have added an output the to Console to indicate when the background task has completed and when, therefore, you can close the sample.

Locking, Blocking, Racing and Deadlocking

Queuing a work item in the thread pool or calling the Thread.Start method does not mean that the background task will start to execute immediately. If several threads are created to run background tasks they will not, necessarily, actually start in the order in which they were created and started. You cannot be sure how much code will be executed in a thread's time slice. If you consider the previous statements you will soon realise that running extra threads in an application may not be as straightforward as it seems. A lot of background tasks rely on and alter class level state fields, either directly or through properties, if a state field can be altered by multiple threads, perhaps concurrently, then a way is needed to control or synchronize thread access to the field to ensure that only one thread at time is allowed to alter the field. Sections of code that alter a resource, such as a class level field, that is shared between threads are known as critical sections, thread access to critical sections of code must be synchronized.

The code sample below shows a class level field being updated by two threads. The sample output shown below was generated on a dual processor machine:

ExpandC# multithread shared field update
ExpandVB multithread shared field update
ExpandThe sample code above generates the following output

The output shows that the threads t1 and t2 are both executing and changing the _count field at the same time. It is pretty clear from the code in the AdjustCount method that this is not what is intended, rather than alternating between 0 and 1, as expected, one thread (eventually) cancels out the other thread and _count is reported as remaining at 0. To get the AdjustCount method to work as intended we need to synchronize access to the loop, so that only one thread at a time is allowed inside the loop.

The code sample below shows a synchronized version of the AdjustCount method:

ExpandC# synchronized multithread shared field update
ExpandVB synchronized multithread shared field update
ExpandThe sample code above generates the following output

In the revised AdjustCountSync method we have synchronized the critical section of code i.e. the loop in which the shared field is updated, with the Monitor class. The CSharp lock and the Visual Basic SyncLock statements are expanded by the relevant compiler to execute the Monitor.Enter and Monitor.Exit methods within a try, finally block. When the first thread reaches the critical section, it acquires a lock and then enters the section and can remain within the section, uninterrupted, until it has finished its work. When the second thread reaches the critical section it cannot acquire a lock and so it is queued until the first thread releases its lock, when that happens the second thread can then acquire a lock and enter the critical section. The output from the revised AdjustCountSync method clearly shows that only one thread at a time is within the critical section of code and the method is working as expected, _count is alternating between 0 and 1.

When using the Monitor class, whether explicitly or implicitly with the lock (SyncLock in Visual Basic) statement, don't be tempted to use the this (Me in Visual Basic) reference as the target of the lock or the class type for static data, as these can both cause problems when used with public types. Declare a private class level Object field as a lock target or a private static (Shared in Visual Basic) field for static data. The Monitor class is very easy to use, especially with the lock or SyncLock statements but there are several other techniques worth considering, some of which are much more fine grained and/or more efficient than the Monitor. We strongly recommend that you take some time to study the various classes in the System.Threading namespace and we particularly recommend the Interlocked class, as it provides very efficient methods for assigning or exchanging, incrementing and decrementing variables in an atomic and therefore thread-safe manner.

A thread that is queued on a synchronization lock, waiting for an object to be signalled or waiting for a method call to return is said to be blocking. A blocked thread cannot continue executing until the relevant lock is released, the wait object is signalled or the method call returns. Therefore multithreaded code that contains a lot of synchronized code sections can, when thread scheduling is taken into account, run more slowly than similar code running on a single thread. Designing and coding a multithreaded application to produce the expected results, efficiently and reliably can be challenging, get it right and you will have an application that really flies, however, get it wrong and you will be in debugging hell trying to figure out why the application works fine sometimes but occasionally either runs like a slug or produces results that are garbage.

Another reason for the need to synchronize threads is the situation where two or more threads are racing to a particular piece of code and the result of the operation relies on which thread gets to the specific piece of code first. This is known as a race condition and is either a design flaw or a bug, depending on which side of the fence you sit! A fairly typical indication that a multithreaded application is suffering from a race condition is when the application does not produce consistent results, from known input when it is run for a long period of time and when the result of a given run cannot be predicted. Faced with this situation, the threads accessing the relevant section of code need to be synchronized, so that you can be sure the threads always access the code in the correct order or better still the problem needs to completely designed out i.e. the race condition has to be eliminated.

There is just one last topic for the locking and blocking, doom and gloom, section, consider the situation where each of two threads tries to acquire a lock on a resource that the other thread has already locked. This is known, unsurprisingly, as a deadlock, in this situation neither of the two threads can make further progress. It is tempting to assume that this type of situation is relatively easy to avoid but on large projects with several or many developers the big picture can become obscured or lost entirely and if it does, potential deadlocks can creep into the code and they won't always show up in testing, however, you can bet that they will show up as soon as the system is out of the door.

BackgroundWorker Component

The BackgroundWorker component is located in the System.ComponentModel namespace and derives from the System.ComponentModel.Component class. If you are using Visual Studio 2005 you can add a BackgroundWorker component to your project by dragging it from the toolbox onto a form. The BackgroundWorker will appear in the component tray and you can use the properties windows to set the relevant properties and hook up the events. You can also use the BackgroundWorker component directly from code by creating an instance of the class, setting the properties and hooking up the relevant event handlers. Therefore the BackgroundWorker component is a good choice for running a background task directly in a form, whether or not that is a good design choice we will leave you to decide, however, there is nothing to stop you from using the BackgroundWorker component in any class, as we will demonstrate later in the article.

The BackgroundWorker component uses the System.ComponentModel.AsyncOperationManager class to retrieve an AsyncOperation which it uses to manage the background operation. The AsyncOperation class uses the System.Threading.SynchronizationContext class to ensures that the event handlers are called on an appropriate thread without interrupting the asynchronous operation i.e. the thread running the asynchronous operation does not block whilst waiting for the event handlers to finish. This is a point worth noting when you are designing your asynchronous operation and your event handlers, you need to balance the interval at which you are raising a progress event with the amount of work that the progress event handler is doing to avoid saturating your application with event calls.

When using the BackgroundWorker in a (standard) Windows Forms application, the ProgressChanged and RunWorkerCompleted event handlers are called on the main UI thread enabling the direct updating of UI controls. However, beneath the surface the BackgroundWorker class uses an asynchronous delegate to execute the background operation, therefore, its DoWork event handler, which is used to run the background operation, is called on a thread from the thread pool and cannot, therefore, be used to directly update a UI control.

You can download the source code, using the link provided in the "Article Options" on the left of the page, as part of either a CSharp or a Visual Basic demonstration Visual Studio 2005 project. The source code includes a demonstration of the BackgroundWorker component being used to run a background task from within a form. We do not have the space to show the full listing here but we will endeavour to show the important methods.

The code sample below shows how to run a background task using the BackgroundWorker component, some helper methods have been omitted for clarity:

ExpandC# background task using the BackgroundWorker component
ExpandVB background task using the BackgroundWorker component

The sample code is contained in a Form class and consists of two Button.Click events for running and cancelling the background task, handlers for the BackgroundWorker DoWork, ProgressChanged and RunWorkerCompleted events and the LongTask method that simulates a background task. The form also contains a Label (labelWorkerStatus) to display a status message, another Label (labelWorkerProgress) to display a progress message and a ProgressBar (progressBarWorker) to display the progress of the background task.

To start the background task, the run button click event handler, creates a new instance of the BackgroundWorker class, sets its WorkerReportsProgress and WorkerSupportsCancellation properties to True, so that we can raise the ProgressChanged event and if necessary cancel the background task. The handler then sinks all three of the BackgroundWorker events and calls the BackgroundWorker.RunWorkerAsync method to start the background task. There is another overload of the RunWorkerAsync method that accepts an argument Object, if you use this overload the DoWorkEventArgs that is passed to the DoWork event handler will contain the specified argument Object when it is called by the BackgroundWorker.

The DoWork event is handled by the BackgroundWorkerDoWork method, this method is called on a background thread and therefore we cannot directly update any UI controls from the code executed by this method. The sample just calls the nonsense LongTask method to simulate a background task, the method just runs for one minute and raises the ProgressChanged event at five percent intervals. There are, however, several points worth noting in the LongTask method, which is why we have included it in the listing. The method does not use the class level BackgroundWorker field reference (_worker), instead it uses the instance reference passed to the handler, this decouples the handler code from the form field and allows the background task to be used with any BackgroundWorker instance.

The method checks the BackgroundWorker.CancellationPending property, before doing anything and returns immediately if it is True. The method then enters a loop to simulate work, within the loop, the CancellationPending property is checked again on each iteration and the loop exits and therefore, the method returns, if at any point the property returns True. At five percent intervals the BackgroundWorker.ReportProgress method is called to raise the ProgressChanged event. Finally, when the LongTask method returns to the BackgroundWorkerDoWork method, the CancellationPending property is checked again and the DoWorkEventArgs.Cancel property is set to True if the background task has been cancelled. This innocuous looking piece of code highlights one of the potential problems of working with multiple, concurrent, threads, if the CancellationPending property is set to True just as the LongTask method is finishing, this piece of code will flag the task as cancelled even though, in reality, it has finished. We don't think that this would be an issue in this case, as the original caller would be expecting the task to be cancelled. However, if it was an issue, we could, for example, change the LongTask method to return a Boolean indicating whether or not it had been cancelled or we could pass the DoWorkEventArgs to the LongTask method and remove the check from the BackgroundWorkerDoWork method.

The ProgressChanged event is handled by the BackgroundWorkerProgressChanged method, this method is called on the main UI thread, therefore we can directly update the UI controls to reflect the progress of the background task. The BackgroundWorker component raises the ProgressChanged event, if its WorkerReportsProgress property is set to True, whenever its ReportProgress method is called from the background task.

The RunWorkerCompleted event is handled by the BackgroundWorkerRunWorkerCompleted method, this method is called on the main UI thread, therefore we can directly update the UI controls to reflect the outcome of the background task. The BackgroundWorker component raises the RunWorkerCompleted event when the background task returns, when the background task is cancelled or when the background task throws an exception. Therefore, the handler code should check for all of these conditions to ascertain the outcome of the background task.

To cancel the background task, the cancel button click event handler calls the BackgroundWorker.CancelAsync method. The CancelAsync method will, if the WorkerSupportsCancellation property is set to True, set the CancellationPending property to True. The background task should periodically check the CancellationPending property and should stop execution and return if the property is set to True. No matter how diligent the background task is in checking the CancellationPending property it is still possible for it to miss the property being set to True, occasionally, the background task will finish its work and return without noticing that the task has been cancelled. In this situation, the RunWorkerCompletedEventArgs passed to the RunWorkerCompleted event handler will not the show the task as cancelled. Although not infallible, the technique of using a cancel flag to prematurely stop a task running on a background thread is one of the better options, aborting one thread from another can be unpredictable and therefore care should be taken when, for example, using the Thread.Abort method.

Asynchronous Methods

When you call a method that is either in the same class or in another class, by default, the call is synchronous, the code after the method call does not execute until the called method finishes its work and returns. A call to an asynchronous method however, always returns immediately, the called method starts a background thread on which to perform its work and then returns to the caller, the caller can continue to execute on its thread whilst the called method executes concurrently (on multiprocessor or multi-core machines) on a background thread. Asynchronous methods are, typically, implemented to run background or long running tasks. When designing the implementation of background or long running tasks, classes or components with asynchronous methods can be used to hide the potential complexity of multithreading from calling code.

Asynchronous delegates are frequently used to execute methods on a background thread, they are fairly straight forward to use and further abstract the potential complexity of creating, managing and running threads. An asynchronous delegate i.e. a delegate that implements BeginInvoke and EndInvoke methods, is executed, by the common language runtime (CLR), on a thread from the thread pool. If a call-back method is specified, when creating the delegate, it is called, when the asynchronous operation is complete, on the thread pool thread. If the asynchronous task raises events, to report progress, for example, the event handlers will, by default, be called on the thread pool thread.

Using an asynchronous delegate's BeginInvoke method you can call any method asynchronously, as the CLR will retrieve a thread from the thread pool and use it to call the delegate's target method. Before using this technique to call methods that have not been specifically designed to run asynchronously, however, you should make sure that you have a good understanding of what the target method does and how it does it, to ensure that it is suitable for running on a background thread.

IAsyncResult Design Pattern

The IAsyncResult Design Pattern is defined as the implementation of two methods BeginMethodName and EndMethodName that begin and end the asynchronous operation MethodName. After calling BeginMethodName, the calling code can continue to execute on the calling thread, whilst the asynchronous operation executes a different thread. Each call to BeginMethodName must be matched with a call to EndMethodName to retrieve the results of the operation. When the BeginMethodName method is called, the common language runtime (CLR) queues the request and returns immediately. The MethodName method is called as soon as possible on a thread from the thread pool, the original calling method can continue to execute, on its own thread, in parallel with the MethodName method which is executing on a thread pool thread.

If a call-back method has been specified in the call to the BeginMethodName method, the call-back method will be called when MethodName returns. In the call-back method, a call to the EndMethodName method must be made to end the asynchronous operation and retrieve the return value and any out parameter values. If no call-back method is specified when calling BeginMethodName, the EndMethodName method can be called from the thread that called BeginMethodName. When not specifying a call-back method, the caller can determine when MethodName has completed and therefore when to call EndMethodName either by waiting on or polling the operation using the IAsyncResult returned from the BeginMethodName method.

You can download the source code, using the link provided in the "Article Options" on the left of the page, as part of either a CSharp or a Visual Basic demonstration Visual Studio 2005 project. The source code includes a demonstration of the IAsyncResult Design Pattern implemented in a class named AsyncClass. We do not have the space to show the full listing here but we will endeavour to show the important methods and the relevant calling code.

The code sample below shows how to run a background task using the IAsyncResult Design Pattern. Some methods and properties have been omitted for clarity and brevity:

ExpandC# IAsyncResult Design Pattern
ExpandVB IAsyncResult Design Pattern

The sample code is contained in a demonstration class that implements BeginLongTask and EndLongTask methods that execute the LongTask method asynchronously. The LongTask method is virtually the same as the BackgroundWorker example, however, unlike the BackgroundWorker example, it raises the ProgressChanged event directly on the background thread. When using an asynchronous delegate directly, any event handlers that we call and the call-back method are all called on the background thread. Forcing the client or calling code to marshal the data in the event handlers is not a particularly user friendly way of implementing an asynchronous operation in a class or component. It could be argued that when, as in this example, the class or component is in the same assembly as the calling code and therefore both are likely to be coded by the same developer, that this is an acceptable solution. However, when developing a class library class or component that is to be used in multiple projects we strongly recommend that you consider implementing the Event Based Asynchronous Design Pattern rather than the IAsyncResult Design Pattern. The Event Based Asynchronous Design Pattern involves a bit more work on the class or component side but it makes the asynchronous class or component much easier for client or calling code to use.

The client or calling code in the sample is contained in a Form class that has two Button.Click event handlers, one to run the asynchronous method and one to, optionally, cancel it. The run handler creates an instance of the demonstration AsyncClass, sinks its ProgressChanged event and then calls the BeginLongTask method, passing in an AsyncCallback delegate representing the method that is to be called when the task has completed. The IAsyncResult that is returned by the BeginLongTask method can be used to wait for the task to complete or to poll for the task to complete. However, as both of these options can impact the performance of the UI, we have opted to specify a call-back method, so that the UI will remain responsive whilst the task is executing in the background. The AsyncProgressChanged method handles the AsyncClass.ProgressChanged event and the AsyncTaskCallback method will be called by the asynchronous delegate when the task has completed. Both of these methods will be called on the background thread and both update UI controls, therefore, both use the Control.Invoke method to perform UI control updates on the correct thread. The AsyncTaskCallback method calls the EndLongTask method to end the asynchronous task, each call to the BeginLongTask method must be matched by a call to the EndLongTask method. Although we are not doing so in the sample, the EndMethodName implementation can have ref or out parameters and/or a return value to retrieve the results of an asynchronous task.

Event Based Asynchronous Design Pattern

The Event Based Asynchronous Design Pattern is defined as the implementation of one or more methods named MethodNameAsync. These methods may mirror synchronous methods that perform the same operation on the current thread. The class implementing the MethodNameAsync method may have a MethodNameCompleted event and a MethodNameCancelAsync method. The implementation of the event based asynchronous design pattern may take several forms, the simplest class may have a single MethodNameAsync method and a corresponding MethodNameCompleted event. More complex implementations may have several MethodNameAsync methods, each with a corresponding MethodNameCompleted event. Classes may also, optionally, support the cancellation of an asynchronous operation, progress reporting and incremental results for each asynchronous operation. A more advanced implementation may support multiple pending calls (multiple invocation) allowing an asynchronous method to be called many times before it completes other pending operations.

AsyncComponent Base Class

To demonstrate an implementation of the Event Based Asynchronous Design Pattern we have developed the AsyncComponent class. The AsyncComponent class extends the System.ComponentModel.Component class to provide a component base class with built in support for executing asynchronous methods. We can't show all of the code in the AsyncComponent class in this article but we will outline the specification, describe how to use AsyncComponent and describe the functionality of the main, protected and public, methods.

You can download the source code, using the link provided in the "Article Options" on the left of the page, for the AsyncComponent class and its associated event argument data classes as part of either a CSharp or a Visual Basic demonstration Visual Studio 2005 project. The source code is intended, primarily, to demonstrate an implementation of the Event Based Asynchronous Design Pattern, however, you can make use of the code either in full or in part in your own applications without charge (see the copyright and disclaimer notice in the AsyncComponent class for full details).

The AsyncComponent base class can be used, by a derived class, to execute multiple methods asynchronously, however, only one asynchronous method or task can be running at a time. The AsyncComponent class provides functionality that allows a derived class to pass user state, as part of the event data, to both the ProgressChanged event and the TaskCompleted event, therefore the original caller can determine which asynchronous method is reporting progress or has just completed. You should note, however, that user state, as implemented in the AsyncComponent class is intended to allow the original caller to identify a single invocation of a particular method rather than to identify a particular invocation of a multiple invocation method. The difference may, at first glance, seem subtle but it is important to realise that the AsyncComponent class does not allow a specific method to be executed multiple times concurrently, in addition the AsyncComponent class does not allow multiple, different, methods to execute concurrently.

The AsyncComponent class contains the BackgroundWorker class and surfaces some of its properties, methods and events, some in renamed form, publicly to external callers and the Visual Studio 2005 properties window. Protected methods are provided to allow a derived class to start, manage and execute an asynchronous task using the contained BackgroundWorker.

The BackgroundWorker class, contained by the AsyncComponent class, does not override the protected Component.Dispose method of its base class and therefore, as it does not need to call Dispose on its contained BackgroundWorker class, there is no need for the AsyncComponent class to override the protected Dispose method of its Component base class. However, an AsyncComponent derived class should override the Component.Dispose method if it has resources that require disposing.

The AsyncComponent class has the following public properties:

CancellationPending
A virtual (Overridable in Visual Basic) read only property that delegates to the BackgroundWorker.CancellationPending property. If there is no BackgroundWorker reference when the property is called it returns False.
IsBusy
A virtual (Overridable in Visual Basic) read only property that delegates to the BackgroundWorker.IsBusy property. If there is no BackgroundWorker reference when the property is called it returns False.
ReportsProgress
Gets or sets whether the AsyncComponent reports progress i.e. whether or not it raises the ProgressChanged event. The default value is False.
SupportsCancellation
Gets or sets whether the AsyncComponent allows an asynchronous task to be cancelled. The default value is False.

The AsyncComponent class has the following protected methods:

virtual void OnProgressChanged(ProgressChangedEventArgs e)
A virtual (Overridable in Visual Basic) method that raises the ProgressChanged event. This method will always be called on a thread that is appropriate for the original caller, typically, that will be the main UI thread.
void ReportProgress(Object taskId, Int32 percentProgress)
void ReportProgress(Object taskId, Int32 percentProgress, Object userState)
Derived classes should call this method, from within their asynchronous task method, to raise the ProgressChanged event on a thread that is appropriate for the original caller. Ultimately a call to this method results in an asynchronous internal call to the OnProgressChanged method on the relevant thread. The internal call to the OnProgressChanged method is only made if the AsyncComponent.ReportsProgress property is set to True.
virtual void RunTask(Object taskId, RunTaskArguments arguments)
A virtual (Overridable in Visual Basic) method that is called on a background thread. Derived classes should override this method and run their asynchronous method from it. The taskId parameter is the same value that was passed to the RunTaskAsync method and should be used by a derived class to identify the method to execute. The arguments parameter contains the data needed to execute the asynchronous task, such as, the argument object that was passed to the RunTaskAsync method and properties that a derived class can set to specify if the task was cancelled, the result of the task and the state object passed in by the original caller. A derived class can call the ReportProgress method periodically during the execution of the asynchronous task, without blocking the task, to raise the ProgressChanged event. The task should also monitor the CancellationPending property to check if the task has been cancelled by the original caller. When this method returns, the AsyncComponent class will raise the TaskCompleted event.
void RunTaskAsync(Object taskId)
void RunTaskAsync(Object taskId, Object argument)
A derived class should call this method to start an asynchronous task. The taskId parameter can be used to uniquely identify the task that is to be executed. The argument object will be passed, together with the taskId, in the RunTaskArguments data that is passed to the RunTask method. Ultimately a call to this method will result in an asynchronous internal call to the RunTask method on a background thread.
Boolean TaskExists()
A utility method that a derived class can use to check if a background task currently exists. Returns True if a background task exists, whether it is running or not, False if no task exists.

The AsyncComponent class has the following public methods:

virtual void CancelAsync()
A virtual (Overridable in Visual Basic) method that can be used by the original caller of an asynchronous method in a derived class to cancel the task. Will set the CancellationPending property to True, if the AsyncComponent.SupportsCancellation property is True at the time of the call.

The AsyncComponent class has the following public events:

ProgressChanged
The ProgessChanged event is raised, by a derived class, by calling the AsyncComponent.ReportProgress method. The event handler is passed a System.ComponentModel.ProgressChangedEventArgs instance containing the event data and will be called on a thread appropriate to the client, typically this will be the main UI thread. The ProgressChanged event is only raised if the AsyncComponent.ReportsProgress property is set to True.
TaskCompleted
The TaskCompleted event is raised by the AsyncComponent when the RunTask method returns. The event is raised when the asynchronous task completes, is cancelled or throws an exception. The event handler is passed a TaskCompletedEventArgs instance containing the event data and will be called on a thread appropriate to the client, typically this will be the main UI thread.

As stated earlier in the article, the sample source code Visual Studio 2005 project contains the AsyncComponent class and its associated event data classes. The project also contains a class named CustomComponent, that derives from AsyncComponent, to demonstrate how to use the AsyncComponent class.

If you want to use AsyncComponent in one of your own projects proceed as follows:

  • Add the following classes to the project: AsyncComponent, RunTaskArguments and TaskCompletedEventArgs.
  • From the Visual Studio 2005 Project menu, select New Item.
  • In the New Item dialog, select the Component Class template and name the new class.
  • When the new class has been added to the project, change the base class from Component to AsyncComponent. Note: If you are using Visual Basic the base class declaration will be in the designer file. To open the designer file, select the project in the solution explorer and click the Show All Files (solution explorer toolbar) button. If you expand your new class file you will see the <className>.Designer.vb file. Select the designer file and click the View Code (solution explorer toolbar) button.
  • In the new component class, create the MethodName method that is to be the background task, the method can be private, protected or public. Don't forget to check the base class CancellationPending property periodically in your code. You can, optionally, call the base class ReportProgress method to raise the ProgressChanged event. You can have more than one background task method but only one can execute at a time.
  • Create the MethodNameAsync method that is to be called by client code to run the task asynchronously. In the method, check that an asynchronous task is not already running by calling the base class IsBusy property and then, if IsBusy is False, call the base class RunTaskAsync method, this is the method that starts the asynchronous task, specifying a unique task ID for the MethodName method.
  • Override the base class RunTask method to run your asynchronous method on a background thread.

Conclusion

The ability to run multiple tasks in parallel has been a desirable feature for server components for a long time and the arrival of the .Net Framework, with its multi-language support for a common framework, has made multithreading easily available to a greater number of developers. We are now, increasingly, seeing multi-core processors and to a lesser extent multiprocessor machines being used as everyday workstations, therefore, multithreading in desktop applications and services is also becoming a desirable and worthwhile feature. If you are developing a Windows Forms application with fairly modest multithreading requirements then the BackgroundWorker component will get you up and running very quickly using a programming technique that you are already familiar with, namely, events and event handlers. If you are developing a class or component library that is to be used by Windows Forms developers you will have a bit more work to do, however, the .Net Frameworks contains all of the classes that you will need and they are fairly easy to use.