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

Banner

DoEvents and dangers

If you have used the DoEvents command in Visual Basic you might see that this is a very similar mechanism in that it allows the UI thread to process the Dispatch queue but also notice that this is more sophisticated in that another thread is used to do the work.

Just as there were dangers in using DoEvents and similar constructs there are even more dangers in using async/await. For example, as the UI stays responsive, the user can click the same button more than once and create multiple tasks. Usually this is not a good idea and so the event handler usually disables the button while the task is running:

private async void button1_Click(
object sender, RoutedEventArgs e)
{
button1.IsEnabled = false;
await DoWork();
button1.IsEnabled = true;
}

If you try writing this simple idea using threads and callbacks you will see just how neat the async/await pattern is.

Multiple awaits

This example just gets us started because it raises a few questions of its own. In particular, we still have the problem of allowing the worker code to update the UI. The key factor is that the event handler can update the UI each time it is restarted so we could use an approach where the work is broken into chunks which return to the event handler for an update.

For example:

private async void button1_Click(
object sender, RoutedEventArgs e)
{
button1.IsEnabled = false;
await DoWork();
textBlock1.Text = "First work done";
await DoWork();
textBlock2.Text = "second work done";
button1.IsEnabled = true;
}

In this case what happens is that DoWork runs for 5 seconds and then summons the UI thread back to continue the event handler from where it left off. This results in the text "First work done" being displayed. Then the second await lets the UI thread get on with processing the Dispatcher queue, only to be called back to continue from where it left off 5 seconds later. Hence we see the second work done update message.

OK, so you could break work down into chunks like this and even put the chunks into a for loop, say, to automate the process:

private async void button1_Click(
object sender, RoutedEventArgs e)
{
button1.IsEnabled = false;
for (int i = 0; i < 5; i++)
{
await DoWork();
textBlock1.Text = "Chunk "+i.ToString();
};
button1.IsEnabled = true;
}

However if all you want to do is report the progress of a longer computation via the UI then there is an alternative way of doing the job by passing in a method that can be called on the UI thread by the worker.

Getting results

An even more important issue, however, is how the worker thread can return a result. For simplicity all of the examples so far have returned void, but in real life the worker usually returns some value, usually a complex object.  Getting result back turns out to be remarkably easy.

To return a type t say we simply change the Task to Task<t> and return a t at the end of the code run on the worker thread.

For example to return an int from DoWork you would write:

Task<int> DoWork()
{
return TaskEx.Run(() =>
{
for (int i = 0; i < 10; i++)
{  
Thread.Sleep(500);
};
return 500;
});

where the code run on the new thread returns 500 just to show that it works.

In the asychronous method we simply call the new DoWork in the obvious way:

int result=await DoWork();
textBlock1.Text = result.ToString();

Notice that there are a few points of potential confusion.

The return type of DoWork is stated as Task<int> yet it actually seems to return an <int> to the calling routine. Also there now appear to be two returns in DoWork, the one that returns the Task<int> and the one that returns the int. If you have followed how all of this works then this should seem understandable if potentially confusing.

This article has explained how the new async/await facilities can make writing good UI code easy but the same ideas apply more generally and you can write asynchronous methods that can be called from any thread, not just the UI thread. There are also lots of other facilities - a cancellation mechanism, a progress reporting mechanism and so on - that make the whole even more flexible.

If you would like to be informed about new articles on I Programmer you can either follow us on Twitter, on Facebook , on Digg or you can subscribe to our weekly newsletter.

 

<ASIN:0131857258>

<ASIN:0123745144>

<ASIN:0321694694>

<ASIN:0672330636>

<ASIN:0735626707>


Banner


What's The Matter With Pointers?

Back in the days when C was the language of choice, pointers meant programming and vice versa. Now in the more sophisticated and abstract days of C#, and even C++, raw pointers are a facility that is  [ ... ]



C# Bit Bashing - The BitConverter

Is C# a high-level or a low-level language? It doesn't really matter - all languages are low-level when you are thinking in terms of bits, and sometimes you just can't avoid thinking in bits.


Other Articles



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.