async/await Doesn’t Make Synchronous Code Asynchronous

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).

One thought on “async/await Doesn’t Make Synchronous Code Asynchronous

  1. Roberto Prevato

    Ben, thank You for this post!
    It’s extremely clear and helped me understanding what I want to achieve.

    Best Regards

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *