BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

Parallel and Asynchronous
.NET 4.5+

Async and Await

Asynchronous operations are particularly suited to slow processes that would otherwise block the user interface thread and make applications unresponsive. In the past asynchronous code has been complicated to develop. .NET 4.5 simplifies this greatly.

Synchronous Button

For the first of the three main buttons we'll look at a synchronous operation and the responsiveness problems it generates. When the button is clicked we run a process that simulates a long-running task.

First we disable the button to prevent it from being clicked again until the operation completes. Next we set the style of the progress bar to animate it, suggesting that the program is busy and the user should wait. We then start the slow method, which pauses for ten seconds before returning the time as a string. The return value is applied to the Text property of the corresponding label before halting the progress bar animation and re-enabling the button.

To add this functionality, add the following code, linking the first method to the SynchronousButton's Click event.

private void SynchronousButton_Click(object sender, EventArgs e)
{
    SynchronousButton.Enabled = false;
    SynchronousBar.Style = ProgressBarStyle.Marquee;
    SynchronousLabel.Text = GetTimeSlowly();
    SynchronousBar.Style = ProgressBarStyle.Blocks;
    SynchronousButton.Enabled = true;
}

private string GetTimeSlowly()
{
    Thread.Sleep(10000);
    return DateTime.Now.ToString("HH:mm:ss.fff");
}

Run the program and click the Synchronous button to see the effects. You should see that the application becomes unresponsive, ignoring clicks of the Check Responsiveness button. In addition, the animation of the progress bar will not work and it is possible that you will be unable to reposition the window.

Asynchronous Callback Button

For the second version of the slow method we'll use asynchronous code, based upon delegates and callbacks, as you might have used prior to the availability of .NET 4.5. This will show the additional complexity required, which is one reason that some developers avoid asynchrony in their applications.

We start by creating a delegate that matches the signature of the method that we will be calling asynchronously. Using a delegate gives us a relatively easy way in which we can start the operation on a separate thread. Our method to retrieve the formatted time has no parameters and returns a string, so the delegate is as follows:

delegate string GetTimeDelegate();

We can now add the code that calls the asynchronous method to the button's event handler. In the code below you can see that the button is disabled and the animation for the progress bar is started in the same way as for the earlier example. This part of the code will be synchronous but is executed quickly so will not spoil the user experience.

The third line of the event handler creates a new GetTimeDelegate that is linked to the GetTimeSlowly method, which we have already seen in the synchronous example. The fourth line starts the method linked to the delegate using BeginInvoke. Using BeginInvoke starts the code on a new thread and returns control immediately to the caller. This means that the event handler is processed very quickly.

Note that we pass GetTimeComplete to the first parameter of BeginInvoke. This is the callback method, which will be executed when GetTimeSlowly exits.

private void CallbackButton_Click(object sender, EventArgs e)
{
    CallbackButton.Enabled = false;
    CallbackBar.Style = ProgressBarStyle.Marquee;
    GetTimeDelegate gtd = new GetTimeDelegate(GetTimeSlowly);
    gtd.BeginInvoke(GetTimeComplete, null);
}

Once the ten second delay is over and the time has been returned from GetTimeSlowly, we need to process it using our callback method. This receives an object that implements the IAsyncResult interface. The object provides the functionality we need to obtain the result returned by GetTimeSlowly. We access this information by converting the result to an AsyncResult, obtaining the AsyncDelegate property, casting this to an instance of our custom delegate and running EndInvoke.

Even when we have the result the additional complexity is not over. As the callback will not be executing on the UI thread, it is illegal to update the form's controls directly. Trying to do so will throw a cross-threading exception. Instead we must use the Control.Invoke method. This finds the thread responsible for a control and executes a provided delegate using that thread. In the code below we create a delegate using the MethodInvoker class. The functionality is in a lambda expression that sets the time, stops the progress bar's animation and re-enables the button.

private void GetTimeComplete(IAsyncResult result)
{
    AsyncResult asyncResult = ((AsyncResult)result);
    GetTimeDelegate gtd = (GetTimeDelegate)asyncResult.AsyncDelegate;
    string time = gtd.EndInvoke(result);

    this.Invoke(new MethodInvoker(() =>
    {
        CallbackLabel.Text = time;
        CallbackBar.Style = ProgressBarStyle.Blocks;
        CallbackButton.Enabled = true;
    }));
}

This is a much more complicated way to achieve the same functionality as our first example but in a manner that does not damage the user experience. Try running the updated program and clicking the Asynchronous Callback button. You should find that the animation of the progress bar works correctly and that you can reposition the window and use the Check Responsiveness button without problems.

21 October 2012