await
operator and async
keyword come together:
The asynchronous method in which await is used must be modified by the async keyword.
The opposite is not always true: you can mark a method as async
without using await
in its body.
What await
actually does is to suspend execution of the code until the awaited task completes; any task can be awaited.
Note: you cannot await for async method which returns nothing (void).
Actually, the word 'suspends' is a bit misleading because not only the execution stops, but the thread may become free for executing other operations. Under the hood, await
is implemented by a bit of compiler magic: it splits a method into two parts - before and after await
. The latter part is executed when the awaited task completes.
If we ignore some important details, the compiler roughly does this for you:
public async Task<TResult> DoIt()
{
// do something and acquire someTask of type Task<TSomeResult>
var awaitedResult = await someTask;
// ... do something more and produce result of type TResult
return result;
}
becomes:
public Task<TResult> DoIt()
{
// ...
return someTask.ContinueWith(task => {
var result = ((Task<TSomeResult>)task).Result;
return DoIt_Continuation(result);
});
}
private TResult DoIt_Continuation(TSomeResult awaitedResult)
{
// ...
}
Any usual method can be turned into async in the following way:
await Task.Run(() => YourSyncMethod());
This can be advantageous when you need to execute a long running method on the UI thread without freezing the UI.
But there is a very important remark here: Asynchronous does not always mean concurrent (parallel or even multi-threaded). Even on a single thread, async
-await
still allows for asynchronous code. For example, see this custom task scheduler. Such a 'crazy' task scheduler can simply turn tasks into functions which are called within message loop processing.
We need to ask ourselves: What thread will execute the continuation of our method DoIt_Continuation
?
By default the await
operator schedules the execution of continuation with the current Synchronization context. It means that by default for WinForms and WPF continuation runs in the UI thread. If, for some reason, you need to change this behavior, use method Task.ConfigureAwait()
:
await Task.Run(() => YourSyncMethod()).ConfigureAwait(continueOnCapturedContext: false);