Click here to Skip to main content
15,881,413 members
Articles / Programming Languages / C# 5.0

Understanding await and async through code

Rate me:
Please Sign up or sign in to vote.
4.51/5 (23 votes)
9 Aug 2014CPOL3 min read 53.7K   40   12
The article provides a code first approach to understanding await/async and answers some of the basic questions.

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.

C#
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

  1. 09-August-2014 - Draft version.
  2. 09-August-2014 - Added background

 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Australia Australia
A seasoned software engineer with over 13 years of commercial experience in software design/development.

Comments and Discussions

 
QuestionAwait suspends the execution of the thread where it is used, Then how it will improve performance. Pin
Bhupesh Goyal13-Aug-17 18:55
professionalBhupesh Goyal13-Aug-17 18:55 
AnswerRe: Await suspends the execution of the thread where it is used, Then how it will improve performance. Pin
Michael_Davies13-Aug-17 20:17
Michael_Davies13-Aug-17 20:17 
AnswerRe: Await suspends the execution of the thread where it is used, Then how it will improve performance. Pin
Michael_Davies14-Aug-17 1:50
Michael_Davies14-Aug-17 1:50 
PraiseClear and concise... Pin
Livio Francescucci22-Mar-17 21:31
Livio Francescucci22-Mar-17 21:31 
GeneralMy vote of 5 Pin
Amir Dashti6-Nov-15 4:24
professionalAmir Dashti6-Nov-15 4:24 
GeneralHardly a tip, let alone an article Pin
Clifford Nelson22-Mar-15 14:53
Clifford Nelson22-Mar-15 14:53 
QuestionNice short explanations! Pin
Your Display Name Here17-Mar-15 13:19
Your Display Name Here17-Mar-15 13:19 
SuggestionNeeds emphasis Pin
ArchAngel12328-Oct-14 13:41
ArchAngel12328-Oct-14 13:41 
GeneralRe: Needs emphasis Pin
DotNetCodebased8-Nov-14 23:54
DotNetCodebased8-Nov-14 23:54 
for point 2 that is why you use SynchronizationContext.Current
QuestionWhere is definition of AddAsync? Pin
User 246299114-Oct-14 16:13
professionalUser 246299114-Oct-14 16:13 
AnswerRe: Where is definition of AddAsync? Pin
Talal Tayyab17-Feb-15 13:06
Talal Tayyab17-Feb-15 13:06 
QuestionGood! Pin
Cheranga Hatangala11-Aug-14 13:54
Cheranga Hatangala11-Aug-14 13:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.