Addition one year later
After using async-await for over a year now, I know that some things about async I wrote in my original answer are not correct, although the code in the answer is still correct. Hera are two links that helped me enormously to understand how async-await works.
This interview Eric Lippert shows an excellent analogy for async-await. Search somewhere in the middle for async-await.
In this article, the ever so helpful Eric Lippert shows some good practices for async-await
Original answer
OK, here is a full example that helped me during the learning process.
Suppose you have a slow calculator, and you want to use it when pressing a button. Meanwhile you want your UI to stay responsive, and maybe even do other things. When the calculator is finished you want to display the result.
And of course: use async / await for this, and none of the old methods like setting event flags and waiting for these events to be set.
Here is the slow calculator:
private int SlowAdd(int a, int b)
{
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
return a+b;
}
If you want to use this asynchronously while using async-await you have to use Task.Run(...) to start it asynchronously. The return value of Task.Run
is an awaitable Task:
Task
if the return value of the function you Run is void
Task<TResult>
if the return value of the function you run is TResult
You can just start the Task, do something else, and whenever you need the result of the Task you type await. There is one drawback:
If you want to 'await' your function needs to be async and return Task
instead of void
or Task<TResult>
instead of TResult
.
Here's the code that runs the slow calculator.
It's common practice to terminate the identifier of an async function with async.
private async Task<int> SlowAddAsync(int a, int b)
{
var myTask = Task.Run ( () => SlowAdd(a, b));
// if desired do other things while the slow calculator is working
// whenever you have nothing to do anymore and need the answer use await
int result = await myTask;
return result;
}
Side remark: Some people prefer Task.Factory.StartNew above Start.Run. See what MSDN tells about this:
MSDN: Task.Run versus Task.Factory.StartNew
The SlowAdd is started as an async function, and your thread continues. Once it needs the answer it awaits for the Task. The return value is the TResult, which in this case is an int.
If you have nothing meaningful to do the code would look like this:
private async Task`<int`> SlowAddAsync(int a, int b)
{
return await Task.Run ( () => SlowAdd(a, b));
}
Note that SlowAddAsync is declared an async function, so everyone who uses this async function should also be async and return Task or Task<TResult
>:
private async Task UpdateForm()
{
int x = this.textBox1.Text;
int y = this.textBox2.Text;
int sum = await this.SlowAddAsync(x, y);
this.label1.Text = sum.ToString();
}
The nice thing about async / await is that you don't have to fiddle with ContinueWith to wait until the previous task is finished. Just use await, and you know the task is finished and you have the return value. The statement after the await is what you'd normally do in the ContinueWith.
By the way, your Task.Run doesn't have to call a function, you can also put a statement block in it:
int sum = await Task.Run( () => {
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
return a+b});
However the nice thing about a separate function is that you give those who don't need / want / understand async, the possibility to use the function without async / await.
Remember:
Every function that uses await should be async
Every async function should return Task or Task<Tresult
>
"But my event handler can't return a Task!"
private void OnButton1_Clicked(object sender, ...){...}
You're right, therefore that is the only exception:
async event handlers may return void
So when clicking the button, the async event handler would keep the UI responsive:
private async void OnButton1_Clicked(object sender, ...)
{
await this.UpdateForm();
}
However you still have to declare the event handler async
A lot of .NET functions have async versions that return a Task or Task<TResult
>.
There are async functions for
- Internet access
- Stream Read and Write
- Database access
- etc.
To use them you don't have to call Task.Run, they already return Task and Task<TResult
> just call them, continue doing your own stuff and when you need the answer await for the Task and use the TResult.
Start several tasks and wait for them to finish
If you start several tasks and you want to wait for all of them to finish, use Task.WhenAll(...) NOT Task.Wait
Task.Wait returns a void. Task.WhenAll returns a Task, so you can await for it.
Once a task is finished, the return value is already the return of the await, but if you await Task.WhenAll ( new Task[]{TaskA, TaskB, TaskC});
you have to use the Task<TResult
>.Result property to know the result of a task:
int a = TaskA.Result;
If one of the tasks throws an exception, it is wrapped as InnerExceptions in an AggregateException. So if you await Task.WhenAll, be prepared to catch the AggregateException and check the innerExceptions to see all exceptions thrown by the tasks you started. Use the function AggregateException.Flatten to access the exceptions more easily.
Interesting to read about cancellation:
MSDN about Cancellation in managed threads
Finally: you use Thread.Sleep(...). The async version is Task.Delay(TimeSpan):
private async Task`<int`> MySlowAdd(int a, int b)
{
await Task.Delay(TimeSpan.FromSeconds(5));
return a+b;
}
If you use this function your program keeps responsive.
await
applies to expressions that have a suitableGetAwaiter()
implementation, whichTask
does - soawait
applies toTask
(in a sense) – Scholzawait TaskEx.Delay(5000);
, but you'll have to study more for your general case. Theawait
keyword looks forGetAwaiter
, and basically anything that provides a suitableGetAwaiter
can be used with await. Jon Skeet has a great series in his blog disecting await in great detail. – Winther