C# Corner

Asynchronous Programming in .NET: I'll Call You Back

VSM Web columnist Eric Vogel kicks off his first C# Corner installment with a walk through creating an application using asynchronous programming.

Asynchronous programming is a means of parallel programming in which a unit of work runs separately from the main application thread and notifies the calling thread of its completion, failure or progress. You may be wondering when you should use asynchronous programming and what are its benefits and problem points.

The main benefits one can gain from using asynchronous programming are improved application performance and responsiveness. One particularly well suited application for the asynchronous pattern is providing a responsive UI in a client application while running a computationally or resource expensive operation. Today we will get started with the basics so you can learn how to become an asynchronous programming ninja and round house kick your multi-threading woes.

The .NET Framework provides a few avenues to get on the ramp to asynchronous programming. Some of your implementation choices from the most basic to complex include using background workers, invoking a method asynchronously from a delegate, or implementing the IAsynchResult interface. All of these options allow you to multi-thread your application without ever having to manage your own threads. The .NET Framework asynchronous APIs handle this drudgery for you.

Setting up our Application
What better way to learn asynchronous programming than to create an application? Our sample application will compute the factorial of a number while maintaining a responsive user interface. The user will be displayed a status message of "Calculating" while the factorial is being calculated. When the factorial has finished being calculated, the status message will be updated to "Completed" and the result will be updated.


[Click on image for larger view.]
Figure 1.

Open up Visual Studio and create a new WPF C# Application. Open up MainWindow.xaml and place the markup below in the Window element content.

  <Grid>
<StackPanel>
<StackPanel Orientation="Horizontal">
<Label>Number:</Label>
<TextBox Name="txtArg" MinWidth="100"></TextBox>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Name="lblCompute">Result:</Label>
<Label Name="lblResult"></Label>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label>Status</Label>
<Label Name="lblStatus"></Label>
</StackPanel>
<Button Name="btnCompute" Click="btnCompute_Click">Compute</Button>
</StackPanel>
</Grid>

Next create a new C# Library project and name it Common. Add a new class named Math to the Common library. The Math class will contain our Factorial function that will be used by all of the examples. The factorial of a number (n!) is defined as the product of n with all positive integers less than n. The factorial of 0 is defined as 1.

public static ulong Factorial(ulong number)
{
System.Threading.Thread.Sleep(25);

if (number == 0)
{
return 1;
}

return number * Factorial(number - 1);
}

Now on to the fun part of wiring up our application to call the Factorial function asynchronously.

Creating a Background Worker
The most basic asynchronous component available in .NET is the background worker. It is also the easiest to use and is well suited for many problems. The background worker component allows you to start, update and cancel an asynchronous operation. When the user clicks on the Computer button we will initiate our call to the factorial function and set up a callback function. While the task is being performed we will also notify the user that the action is pending. All the while the task is being processed, the user may still interact with the application.

Open up your MainWindow.xaml.cs file.

First of all include the System.ComponentModel namespace.

using System.ComponentModel;

Next construct your background worker component.

BackgroundWorker _bw = new BackgroundWorker();

Next we will wire up our DoWork and RunWorkerCompleted event of the background worker with our long running function. The DoWork event will be fired when RunWorkerSync method is called on the background worker. The RunWorkerCompleted event will be fired when the background worker's DoWork event finishes.

_bw.DoWork += new DoWorkEventHandler(bw_DoWork);
_bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_bw_RunWorkerCompleted);


void bw_DoWork(object sender, DoWorkEventArgs e)
{
long number = (long)e.Argument;
e.Result = Factorial(number);
}

Next we instruct the background worker to run our computation in our button's click event and display the "Calulating... " status message.

  private void btnCompute_Click(object sender, RoutedEventArgs e)
{
long arg = long.Parse(txtArg.Text);
lblStatus.Content = "Calculating...";
_bw.RunWorkerAsync(arg);
}

Finally we update out result label in the background worker's RunWorkerCompleted event.
void _bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
lblResult.Content = e.Result;
lblStatus.Content = "Completed";
}

Calling a method asynchronously from a delegate
Our next available option in .NET is to implement our own asynchronous events. Let's go over how to implement the previous example using an asynchronous delegate in place of the background worker component. First create a delegate that will return a long and take in a long parameter.

Func<long, long> _factorialFunc;

Next assign the delegate to the Factorial function.

   _factorialFunc = Factorial;

Finally we invoke the delegate in the button's click event, passing in a callback function that will be called when the asynchronous function completes.

private void btnCompute_Click(object sender, RoutedEventArgs e)
{
long arg = long.Parse(txtArg.Text);
lblStatus.Content = "Calculating...";
_factorialFunc.BeginInvoke(arg, FactorialCompleted, null);
}

void FactorialCompleted(IAsyncResult asynchResult)
{
Func<ulong, ulong> target = (Func<ulong, ulong>)asynchResult.AsyncState;
ulong result = target.EndInvoke(asynchResult);
Dispatcher.BeginInvoke(new Action<ulong>(UpdateResult), result);
Dispatcher.BeginInvoke(new Action<string>(UpdateStatus), "Completed");
}

void UpdateResult(ulong result)
{
lblResult.Content = result;
}

void UpdateStatus(string status)
{
lblStatus.Content = status;
}

The FactorialCompleted callback function retrieves the factorial result from the delegate and sets the result and status labels on the form.

Implementing the IAsynchResult Interface
Our third and most complex asynchronous programming option is to implement the IAsychResult interface and create BeginX and EndX versions of your operation. The BeginX and EndX operations should perform the operation asynchronously. The IAsynchResult interface includes an optional AysncState property that may contain information about the operation being performed. The AsynchWaitHandle property is used to have the operation run synchronously. The CompletedSynchronously property should be set to true if the operation completed on the same thread it was called upon. The IsCompleted property should be set to true if the operation has finished running.

Let's start out with defining our own implementation of IAsynchResult, which will be utilized by our BeginFactorial and EndFactorial functions. Our implementation of IAsynchResult will be generic and accept an argument and a result of specified types. The code is displayed in Listing 1 below.

Click to view Listing 1.

The BeginFactorial method executes the FactorialAsynch method and passes it the asynchronous object state. The FactorialAsynch method invokes our Factorial method with the argument that is set in the AsynchResult object and calls the SetAsCompleted method once the Factorial function has completed. Additionally, we handle any exception that might occur and pass that to the AsynchResult to handle. The EndFactorial function simply invokes EndInvoke on AsychResult to wait for the function to complete if it is still pending. Finally we update our client application to utilize the BeginFactorial and EndFactorial functions.

  private void btnCompute_Click(object sender, RoutedEventArgs e)
{
Common.Math mathLib = new Common.Math();
ulong number = ulong.Parse(txtArg.Text);
lblStatus.Content = "Calculating...";
IAsyncResult asynchResult = mathLib.BeginFactorial(number, FactorialCallBack, mathLib);
}

private void FactorialCallBack(IAsyncResult asynchResult)
{
Common.Math target = (Common.Math)asynchResult.AsyncState;
ulong result = target.EndFactorial(asynchResult);

Dispatcher.BeginInvoke(new Action<ulong>(UpdateResult), result);
Dispatcher.BeginInvoke(new Action<string>(UpdateStatus), "Completed");
}

void UpdateResult(ulong result)
{
lblResult.Content = result;
}

void UpdateStatus(string status)
{
lblStatus.Content = status;
}

The code is very similar to the asynchronous event example; we call our BeginFactorial function in the computer button click event passing our argument along with a callback and the object state. In the callback function we retrieve the result via the EndFactorial method and update our UI to display the result along with a Completed message.

Conclusion
As you can see asynchronous programming has many benefits but does add complexity to your code. Like all programming patterns it is best to weigh the benefits against the added complexity to see if it is the right fit for your application.

I recommend starting out with the background worker and asynchronous event methods first. In most cases you will not need to implement your own version of IAsynchResult, unless you are running into performance issues with one of the other methods or have another API that is dependent on IAsynchResult.


Reference
Richter, Jeffrey. "Implementing the CLR Asynchronous Programming Model." MSDN | Microsoft Development, Subscriptions, Resources, and More. MSDN, Mar. 2007. Web. 22 Mar. 2011. .

The AsychResult<TArg,TResult> in the listing was inspired by and based upon the AsychResult<TResult> in the referenced article.

comments powered by Disqus

Reader Comments:

Thu, Sep 15, 2011 Victor Marques Hyderabad, India.

Nice tutorial.

Thu, Mar 31, 2011 Dan

Downloaded the code and compared. Not only did you address the long/ulong issue there, but the BeginInvoke() call in the article isn't even correct. Seriously, thanks for taking the time to write the article, but you just wasted 30 minutes of mine by filling it with crap code.

Thu, Mar 31, 2011 Dan

Did you type this from memory or copy it from a working application? If it's the latter, why doesn't your compiler puke on the random mix of long and ulong like mine does? When I correct for that and run it, "target" in FactorialCompleted is always null. Any ideas?

Tue, Mar 29, 2011 Eric Vogel USA

Evan, AsychronousState is an enum that is declared in the AsychResult class file under the AsynchVSMSample.Common namespace. The declaration for AsychronousState is public enum AsynchronousState { Pending, CompletedSychronously, CompletedAsynchronously }

Tue, Mar 29, 2011 MK Tacoma, WA

Tough Room Eric. The background worker is exactly what I was looking for. If I have more than 1 async thread, how would I id them if I use a End_Request method on the page Request manager?

Mon, Mar 28, 2011 Jay

One thing that could improve the example is to disable the button before the RunWorkerAsync method is called. Then when it is done, enable the button. Try clicking on the button twice in a row before making that change. See the exceptions section: http://msdn.microsoft.com/en-us/library/h01xszh2

Mon, Mar 28, 2011 Evan Tallahasse, FL

Your implementation of IAsynchResult is giving me compile errors on AsynchronousState. Either AsynchronousState is a variable that you forgot to declare or its a library which isnt referenced. Can you please offer a fix for this.. I would like to get this example working.

Thu, Mar 24, 2011 Ron Jacobs Redmond, WA

Dude - Using System.Threading.Task for async work - it is way better and is the wave of the future.

Thu, Mar 24, 2011 Kevin

Looks like you got the idea and some of the code for your generic AsyncResult class from MSDN Magazine, March 2007: Implementing the CLR Asynchronous Programming Model (http://msdn.microsoft.com/en-us/magazine/cc163467.aspx). If so, you should give credit and a reference to it.

Add Your Comments Now:

Your Name:(optional)
Your Email:(optional)
Your Location:(optional)
Comment:
Please type the letters/numbers you see above

.NET Insight

Sign up for our newsletter.

I agree to this site's Privacy Policy.