Async, Await and the UI Problem
Written by Mike James   
Thursday, 25 February 2021
Article Index
Async, Await and the UI Problem
The flow of control
Advanced await

The async/await asynchronous programming facilities in .NET solve one of its longstanding problems - how to write an elegant application that uses the UI correctly. But to avoid problems you still need to understand both the problem and the solution.

This is comes from my book on C# - a work in progress.

Deep C#

 front

 Chapter List

  1. Value And Reference
  2. Dynamic C#
  3. Passing Parameters
  4. Inheritance 
  5. Casting – the escape from strong typing
  6. Controlling Inheritance
  7. Delegates
  8. Multicast delegates and events
  9. Anonymous Methods, Lambdas And Closures
  10. Take Exception To Everything
  11. What's The Matter With Pointers?
  12. Generics
  13. Structs
  14. The LINQ Principle
  15. XML in C#
  16. Linq and XML
  17. Regular Expressions in depth
  18. Bit Manipulation
  19. Async, Await and the UI problem
  20. The Invoke pattern
  21. The Parallel For
  22. Deep C# - Custom Attributes In C#  ***NEW
  23. Not so complex numbers in C#
  24. Getting Started With .NET IL
Multicast delegates and events
Tuesday, 25 May 2010
Article Index
Multicast delegates and events
Events
Generic Events

Multicast delegates are useful in their own right but they also form the basis on which the C# event system is built. We take a close look at how they work and how to use them. For example, did you know you could add and subtract delegates?

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 and this is where async and await come into the picture.

Hands-on example

Start a new WPF project and you next need to reference the new assembly.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 ( Thursday, 25 February 2021 )