C# 5.0 Async explained as simple as possible :)
Welcome to another blog post! This time about the new C# Async features! There is already al lot written about these features but after reading all those posts that are already available I still had a couple of questions and not everything was clear.
In this post I will try to explain the new async features in my own style, I will try to keep it as simple and clear as possible. Let’s get started!
First the problem. Let’s assume that I have a very simple WPF application, I want to keep it as simple as possible so a simple calculator will do.
This app is ugly but incredibly simple. So this is a good thing . The code behind shows us that the calculation is a long running task, and the way this is currently implemented will block the UI Thread.
1: private void btnCalculate_Click(object sender, RoutedEventArgs e)
2: {
3: int number1 = int.Parse(txtNumber1.Text);
4: int number2 = int.Parse(txtNumber1.Text);
5: int result = 0;
6: result = Calculate(number1, number2);
7: txtAnswer.Text = result.ToString();
8:
9: }
10: private int Calculate(int number1, int number2)
11: {
12: Thread.Sleep(2000);
13: return number1 + number2;
14: }
The Thread.Sleep on line 12 is to act like a complex task or calculation. As we all know, blocking your UI thread is bad
1: public partial class MainWindow : Window
2: {
3: public MainWindow()
4: {
5: InitializeComponent();
6: }
7:
8: private void btnCalculate_Click(object sender, RoutedEventArgs e)
9: {
10: int number1 = int.Parse(txtNumber1.Text);
11: int number2 = int.Parse(txtNumber1.Text);
12: Calculate(number1, number2);
13: }
14:
15:
16: private void Calculate(int number1, int number2)
17: {
18: Task<int> t = new Task<int>(BackGroundCalculate, new Tuple<int, int>(number1, number2));
19: t.ContinueWith(EndCalculate);
20: t.Start();
21:
22: }
23:
24: /// <summary>
25: /// Method that goes into the task
26: /// </summary>
27: /// <param name="numbersTuple"></param>
28: /// <returns></returns>
29: private int BackGroundCalculate(object numbersTuple)
30: {
31: Thread.Sleep(20000);
32: Tuple<int, int> numbers = (Tuple<int, int>)numbersTuple;
33: return numbers.Item1 + numbers.Item2;
34: }
35:
36: /// <summary>
37: /// ContinuationMethod that gets executed when task finishes, similar to Callback functions from before...
38: /// </summary>
39: /// <param name="number1"></param>
40: /// <param name="number2"></param>
41: /// <returns></returns>
42: private void EndCalculate(Task<int> previousTask)
43: {
44: //to update the UI thread we need to use the dispatcher
45: this.Dispatcher.Invoke(new Action(() => txtAnswer.Text = previousTask.Result.ToString()));
46: }
47: }
Okay! Let’s have a look! The Calculate method on line 16 has been made a bit more user interface friendly as it now instantiates a Task. To the constructor of the Task I pass a method that is going to be executed on a different thread. To show you how much this is like the old BeginInvoke and EndInvoke methods I named this method BeginCalculate. The complex part is that this method is only allowed to have 1 parameter. To pass more than 1 you will have to make a wrapper class or use the Tuple class that comes with .Net 4. The second argument of the task constructor is the actual Tuple containing the two numbers to be added. After the instantiation of the task, on line 19 we hook the task up with a method that get’s called when that task finishes, this very similar to the old BeginInvoke and EndInvoke with a callback method. this callback method starts on line 42 and is called EndCalculate. The callback method must accept a parameter of Task<>, in our case Task<int>. This parameter represents the previous completed task. In EndCalculate we need to update the UI, in WPF this must be done on the UI thread so we need to use the Dispatcher.Invoke method and pass it a lambda that updates the UI on the UI thread. That’s it! Now we have a responsive UI. But……
It cost us a lot of work. And the code did not get more readable because of this. Wouldn’t it be cool if we could write our code without all this multithreading crap all over it, but still have the benefits of the multithreading crap? Microsoft recognized this problem and came up with a solution. This is where the new async and await keywords come into play.
The first thing we need to do is view the calculation as a piece of work that we will start and will complete some time in the future. In some programming languages such a piece is called a Promise( JavaScript) in other programming languages this is called a Future. For C# we call this a Future. The .Net representation of a Future is the Task. Cool! As we are already using those! The line between a Future as a concept and a framework implementation should be clear now. .Net represents this with a Task but WinRT used in Windows 8 uses other classes for this. For C# developers this does not matter, our async and await code stays the same. Let’s look at some code!
First we need to make the Calculate method return a Task. This is easy because we are already using one. We don’t need callback methods anymore with the new async and await keywords, all of that get’s taken care of by the compiler. The picture below shows our new calculate method and the button eventhandler.
Notice how Visual Studio tells us the method is awaitable? All a method needs to do, to be awaitable is to return a Task. It should be a Task that is already started or else nothing will happen. The await keyword does not compiles to a Start() call for tasks. It does compile to a Start() call for the WinRT API’s Futures in Windows 8. To use the await keyword we must first tell the compiler that we are going to use await in this method. That is one of the functions of the async keyword. Let’s have look at this code with await and async in place.
1: private async void btnCalculate_Click(object sender, RoutedEventArgs e)
2: {
3: int number1 = int.Parse(txtNumber1.Text);
4: int number2 = int.Parse(txtNumber1.Text);
5: int result = 0;
6: result = await Calculate(number1, number2);
7: txtAnswer.Text = result.ToString();
8:
9: }
10:
11: private Task<int> Calculate(int number1, int number2)
12: {
13: Task<int> t = new Task<int>(BeginCalculate, new Tuple<int, int>(number1, number2));
14: t.Start();
15: return t;
16: }
On line 1 before the return type, the new async keyword takes it’s place. It tells the compiler that we are going to use the await keyword in this method. The async keyword has one other function that I will explain shortly. On line 6 you see the new await keyword, it should be place before the expression that returns a task. After the await keyword you can see that we can use the result in our code as if there is no multi threading going on! But we still have a responsive UI. So with await and async we can program like we are single threading, everything works, we can use results, catch exceptions and so on, even while in reality these results and exceptions come from a different thread. What is happening? Take a look at figure 3.
Please forgive my paint skills. I will try to explain this figure
– Creating a continuation method
– Hooking it up to the running Task
– Using the Dispatcher to perform work on the UIThread.
It all get’s taken care of by the C# compiler. The second part of our event handler will fire on the UIThread when the Calculate method completes. So cool!
As you can see, the await keyword has two functions. It tells the compiler where to cut our method up, and it allows us to treat a method that returns Task<T> as a method that returns just T. This way, the Task that Calculate returns is not used anywhere in our own code, but the compiler generated code will use that Task.
As I said before, the async keyword also has two functions, it tells the compiler that this method going to be cut in to pieces and when used in a method that returns a Task<T> it allows us to return just T. You can use the async keyword only in methods that have a return type of void (mostly eventhandlers as our button click) and on methods that have a return type of Task, like our Calculate method.
Let’s also make our Calculate method async. Figure 4 shows us how this looks.
As you can see now I have two compiler messages. The error comes from the fact that Calculate should return a Task<int> and because of the async keyword I should not return a Task from my code but just an int. The warning comes from the fact I told the compiler this method is going to be cut up, but I haven’t defined any points for the compiler to cut this method up. I am not obligated to use the await keyword in an async method, if I don’t do this, and for example make the method as simple as return number1 + number 2 that’s fine, but even if I use await when calling this method, it will result in a synchronous call. This brings me to a very important point: Async and await do not provide multi threading for you. The method that get’s awaited needs to shift to a background thread at some point, preferably by creating a Task instance at some point or using an async API call.
Let’s finish the Calculate method. I created a Task instance to do the calculation in the background, but I can’t return that Task anymore because of the async keyword, which causes the compiler to expect simple return statement that returns an integer. The ideal way to do this is by using the await keyword! We saw earlier that the await keyword also allows us to use the result of a task as if there is no task there. Earlier we used the await keyword together with a method that returns a Task, but it can be used with any Task instance. Await just requires a task, it does not matter if that Task comes from a method call or from a variable. Let’s look at the code!
Now everything compiles just fine! It is a bit confusing that an awaitable method use await itself but this is very common in the new framework API’s. You can see that the compiler will need to do some more cutting up. It will cut up the eventhandler in two pieces just like before, but now it will also cut up the Calculate method in 2 pieces. Let’s have a look at the pieces and their order of execution. Figure 6 shows this:
In figure 6 you can see the different parts after compiling and the order in which they execute. First is the button click. In Part 1 the Calculate method will be called. So Calculate’s body will begin to execute until the await statement. The method will return and Callback 2 is assigned as callback method to execute when the calculation finishes. Back in the button click event another await statement is encountered so the button click will also return and Callback 1 is assigned as callback. Because both methods stop executing and a Task is running in the background the UI thread is still responsive! When the Calculation Task finishes, Callback 2 will fire, when Callback 2 finishes Callback 1 will fire on the UIThread and update the textbox. You can see how much work the compiler must be doing to let us program like there is no multithreading going on. All the callbacks get assigned and will fire in the right order. MultiThreading made easy
We are almost done now but before we go I want to make a couple of things clear. First: A method can contain multiple await statements. Let’s say in the button click event I would also want to upload the result to a restfull webservice. In .Net 4.5 many API’s have been updated to return Tasks. So many API calls support await. For example the new HtppClient class.
Here you can see that in our button click there are two async calls. One to Calculate and one to a new UploadResult method. This method also has an await, but this time the await is on an API call. There is one other thing you might notice. The UploadResult has a return type of the non generic Task class. In combination with the async keyword the code in that method must be written like it is a void method. The return type of Task is there to make this method awaitable.
Finally I will make the Calculate method just a little shorter with the new Task.Run method from .Net 4.5. It accepts a delegate and returns a running task so we can write:
In figure 8 I also removed our BeginCalculate method and the contents are pasted in the lambda expression above. No need to use a tuple because a lambda can access outer variabeles in it’s current scope. This way you can use the Task.Run method to easily create your own awaitable async methods!
That’s it! I hope async and await are just as clear now to you as they are to me, let me know!
Regards,
Chris van Beek