Introduction
There are a lot of articles out there that explain the purpose of Await/Async and how it helps simplifying asynchronous programming. However I was always confused with the following:
- Do async/await keywords create separate threads?
- How can await suspend the current method (is the calling thread blocked)?
- How does execution return to the calling method (if the calling thread is blocked)?
The point of this article is to answer these questions in a simple, easy to understand way. This article is meant for developers who are trying to understand asynchronous programming using await/async.
Background
Async and Await keywords were added in .Net 4.5 & C# 5.0 as means to simplify asynchronous programming. The aim was to make it easier for developers to write asynchronous code and move the repetitive tasks to the compiler.
The earlier asynchronous programming model (APM) was based on IAsyncResult where asynchronous operations required Begin and End methods. For example the FileStream class implements the BeginRead and EndRead methods.
public override IAsyncResult BeginRead(byte[] array,int offset,int numBytes,AsyncCallback userCallback, Object stateObject)
public override int EndRead(IAsyncResult asyncResult)
The new asynchronous programming model is based on the Task type that uses a single operation to represent the start and completion of the asynchronous operation. The BeginRead and EndRead method have been replaced by a new ReadAsync method.
public Task<int> ReadAsync(byte [] buffer, int offset, int count);
The task based asynchronous pattern is known as TAP.
So now that we have set the base, it is time to discuss the use of await/async and how they can be used simplify task based asynchronous programming.
Code first
Rather than starting with an example and getting lost in its details, we will look directly at code that uses the await keyword.
int sum = await AddAsync(x, y);
int multiply = sum * z;
return multiply;
The code above calls a method that returns the sum of two numbers. The add method is asynchronous in nature and returns a Task<int>
. The signature of the method looks like
public async Task<int> AddAsync(int x, int y)
The await keyword, as you might have read, suspends the current method execution and returns the control to the caller. The keyword does not create any threads. So how does it do this?
From MSDN,
Quote:
An await expression in an async method doesn’t block the current thread while the awaited task is running. Instead, the expression signs up the rest of the method as a continuation and returns control to the caller of the async method
To demonstrate, we can rewrite the original code without using await as
var t = AddAsync(x, y);
var t2= t.ContinueWith((task) =>
{
int sum = t.Result;
int multiply = sum * z;
return multiply;
},TaskScheduler.FromCurrentSynchronizationContext());
return t2;
The await keyword does not create additional threads as it runs on the current synchronization context.
This does answer two important questions.
- No additional threads are created.
- The remaining expressions are registered to continue after the task completes.
The await keyword does most of the magic. The async keyword was mainly added to avoid backwards compatibility problems when using the await keyword. According to a blog from MSDN,
Quote:
Requiring "async" means that we can eliminate all backwards compatibility problems at once; any method that contains an await expression must be "new construction" code, not "old work" code, because "old work" code never had an async modifier.
http://blogs.msdn.com/b/ericlippert/archive/2010/11/11/whither-async.aspx
However the async keyword does have one trick up its sleeve.
public async Task<int> AddAndMultiplyAsync(int x, int y, int z)
{
int sum = await AddAsync(x, y);
int multiply = sum * z;
return multiply;
}
Looking at the method above we see the return type is Task<int>, however we are returning an integer. The magic of course is in the async keyword that changes the return value automatically to Task<T> where T is the type we are returning. If you are playing around with async you might have seen this error pop up
error CS4016: Since this is an async method, the return expression must be of type 'int' rather than 'Task<int>'
This is because if you try to return a Task in an async method, the return type should be Task<Task<T>>.
public async Task<Task<int>> AddAndMultiplyAsync(int x, int y, int z)
{
return AddAsync(x, y);
}
Summary
Does Async/Await create a separate thread?
No – async changes the return value automatically to Task, and allows us to use the await keyword. await registers the remaining method as a continuation and returns control to the caller of the async method.
How can Await suspend the current method?
When we use the await keyword it wraps the remainder of the method call in a Task.ContinueWith block.
How does execution return to the calling method?
As the current method call is wrapped in a Task.ContinueWith block, the await keyword returns the Task<T>
History
- 09-August-2014 - Draft version.
- 09-August-2014 - Added background