Async, Await and the UI problem
Saturday, 30 October 2010
Article Index
Async, Await and the UI problem
The flow of control
Advanced await

The new async/await asynchronous programming facilities in .NET solve one of its long standing problems - how to write and an elegant application that uses the UI correctly.

Banner

 

One of the biggest problems facing any Windows forms or WPF programmer is that you can't use the UI thread to do much work. If you do the result is an unresponsive application. The proper solution is to use a new thread to do all of the heavy computation and leave the UI thread free to get on with what it is supposed to do - deal with user events. However the computation thread usually has to provide evidence that it has done something and this means that it has to interact with the UI components.

As is well known, UI components aren't threadsafe - hence the rule that only the thread that created a component can access it. You can optionally turn this rule off and allow the worker thread to access the UI but this isn't a good idea. The correct way to to allow the worker thread to access the UI is to use the Invoke method to ask the UI Thread to run a delegate that does the update using data from provided by the worker thread.

This may be the correct way but it results in very messy code and given it is such a common requirement we really could do with a simpler way of implementing two or more threads working with the UI. The new C# and VB facilities provided by the Visual Studio Async CTP seem to solve the problem neatly and in a way that is simple enough for even a beginner to user.

Hands-on example

If you want to follow the practical details of the example you will first need to download the CTP from the Microsoft Download site. The installation is automatic but at this early stage things are not completely integrated with Visual Studio. Notice that the CTP works with Visual Studio 2010 or the Express editions of either C# or Visual Basic. It also works in the same way with Silverlight.

After installing the CTP you can start a new WPF project and you next need to reference the new assembly. To do this right click on References and select Add Reference. Then select the Browse tab and navigate to the installation directory - usually

My Documents\Microsoft Visual Studio 
Async CTP\Samples

and select AsyncCtpLibrary.dll 

(AsyncCtpLibrary_Silverlight.dll for a Silverlight project).

Next put a button and two TextBlocks on the form - the button will start the process off and the TextBlocks will record its progress.

First let's look at the problem that we are trying to solve.  The Button's click event handler calls a method that does a lot of work:

private void button1_Click(object sender,
 RoutedEventArgs e)
{
textBlock1.Text = "Click Started";
DoWork();
textBlock2.Text = "Click Finished";
}

You can see that the first two textBlocks are changed to show what is happening. For the purpose of this example DoWork can be simulated by a routine that just loops and so keeps its thread occupied. You get the same overall result if DoWork simply waits for an I/O operation to complete - the important point is that as written it keeps the attention of the UI thread until it is complete. That is:

void DoWork()
{
for (int i = 0; i < 10; i++)
{
Thread.Sleep(500);
}
}

keeps the UI thread busy for 5 seconds and to use Thread.Sleep you also have to add:

using System.Threading;

What do you think you see if you run this program?

If you aren't familiar with the way WPF works you might think that you see "Click Started" appear and then after 5 seconds "Click Finished".

What actually happens is that you see both messages appear after the 5 seconds are up during which time the UI if frozen.

The reason for this behaviour is simply that the UI is frozen from the moment the DoWork method is called and this is usually before the "Click Started" text has been rendered to the display. This is exactly the reason you don't want any intensive computation on the UI thread - in fact you really don't want any computation on the UI thread at all!

Async and await

Now we can look at how to implement this property using the new async and await.

The first thing is to know is that any method that you put async in front of is an asynchronous method, which means it can be started and stopped rather than just run from first to last instruction. We could create a new method and mark is as asynchronous but to keep the example as much like the synchronous case described above we can simply change the Click event handler into an asynchronous method:

private async void button1_Click(
object sender, RoutedEventArgs e)
{
textBlock1.Text = "Click Started";
 DoWork();
textBlock2.Text = "Click Finished";
}

If you do this the compiler will complain that you have an asynchronous method without any awaits and so it will run it as a synchronous method anyway.

To take advantage of the asynchronous nature of the new event handler we have to await the completion of the DoWork method. However if you write:

private async void button1_Click(
object sender, RoutedEventArgs e)
{
textBlock1.Text = "Click Started";
await DoWork();
textBlock2.Text = "Click Finished";
}

Then the compiler will complain that you can't await a method that returns a void. Any method that you await has to return a Task or a Task<T> object where T is the type it would normally return.

In this case as nothing is returned we can use return a Task object:

Task DoWork()

The next question is what Task object do we actually return?

Banner

<ASIN:0596159838>

<ASIN:1430225491>

<ASIN:0672331012>

<ASIN:0321718933>

<ASIN:0735627045>



Last Updated ( Sunday, 31 October 2010 )
 
 

   
RSS feed of all content
I Programmer - full contents
Copyright © 2014 i-programmer.info. All Rights Reserved.
Joomla! is Free Software released under the GNU/GPL License.