With the recent release of .Net 4.5/C# 5, I’ve spent time experimenting with the new async/await functionality. To my surprise, these new keywords didn’t have the effect I anticipated. Based on my skimming of pre-release information, I was under the impression that the appropriate use of these keywords would cause a normally-synchronous method to execute asynchronously.
Wrong!
async/await do not make synchronous code asynchronous. Instead, these keywords make it much easier to code continuations, eliminating ugly boilerplate code.
My Expectation
I thought that awaiting Run’s call to DoWork would cause DoWork to be executed asynchronously, resulting in “After call to DoWork” being outputted before “DoWork Complete.”
using System; using System.Threading; using System.Threading.Tasks; namespace AsyncAwaitExperiment { class Program { static void Main(string[] args) { Run(); Console.WriteLine("After call to DoWork"); Console.ReadKey(); } async static Task Run() { await DoWork(); } async static Task DoWork() { Console.WriteLine("DoWork Starting"); // keep the processor busy for a while for (int i = 0; i < 1000000000; i++) { var total = i * i; } Console.WriteLine("DoWork Complete"); } } }
Nope! In the above code, the only effect of async/await is to generate a warning pertaining to DoWork: “This async method lacks ‘await’ operators and will run synchronously. Consider using the ‘await’ operator to await non-blocking API calls, or ‘await Task.Run(…)’ to do CPU-bound work on a background thread.”
If async/await do not make synchronous code asynchronous, what’s the point of this new language feature?
A Continuation
Change DoWork so that the math computations execute on the thread pool, then lean back and watch async/await work.
async static Task DoWork() { Console.WriteLine("DoWork Starting"); await Task.Run(() => { // keep the processor busy for a while for (int i = 0; i < 1000000000; i++) { var total = i * i; } }); Console.WriteLine("DoWork Complete"); }
In this revised example, after DoWork’s await Task.Run statement is executed, the flow of control passes back to Main instead of waiting for the just-created task to complete. Then when the task completes, the code in DoWork following await Task.Run is executed.
In effect, async/await has taken the code in DoWork after the awaited task and attached it to that task as a continuation. When the task completes, the rest of the code in the method is executed.
To see this in action, run the revised example. First, “DoWork Starting” is outputted, immediately followed by “After call to DoWork” (outputted from inside Main). Then, after a few moments, “DoWork Complete” appears (the continuation is executed).
Ben, thank You for this post!
It’s extremely clear and helped me understanding what I want to achieve.
Best Regards
THANK YOU.
The Syntax of async / await is a bit confusing.
async only STATES that this function COULD fork into a separate thread. But this only occurs if it contains an await ! …. in which case it immediately returns execution to the caller while it awaits that process or processes on a separate thread.
Without an await, the function just runs synchronously as far as the caller is concerned.
This is really quite odd. I’d think that async func would already run in a separate thread, and if you want to wait for it, you’d call await if you want to WAIT for it instead of letting it run asynchronously.
To wait for an async call you have to grab the task, hope it returns something ( Task not just Task) and then call task.Result to grab the result within your thread. Waiting for the result thus causes it to run synchronously to on your thread.
Without a Task, you’d define an intermediary
async Task MyWaiter() { await TrueAsyncFunc(); return 0; }
and then callvar zero = MyWaiter().Result;