An Async/Await Example

by Larry Spencer Monday, October 8, 2012 8:43 PM

.NET 4.5 offers an easy way to maximize the use of your CPU: the async/await pattern. It has the simplicity of single-threading, but feels almost like multi-threading.

The basics of async/await are well-explained on MSDN and elsewhere, so in this post I'd just like to present a brief example that may make the whole thing even clearer. It also lays the groundwork for the next two posts, which will explore some surprising aspects of async/await.

The example uses the simple CodeTimer from my last post so you can see the execution sequence.


class Program
    static void Main(string[] args)

    static async void WriteALot()
        Task task;
        using (new CodeTimer("Calling WriteFileAsync"))
            task = WriteFileAsync();
        using (new CodeTimer("Awaiting WriteFileAsync"))
            await task;

    static async Task WriteFileAsync()
        using (new CodeTimer("Executing WriteFileAsync"))
            int writesDone = 0;
            int writesDesired = 5;
            var bytes = new byte[102400];
            var tempFile = Path.GetTempFileName();
            using (var strm = new FileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.None, 4096, true))
                // Pointlessly write enough data that it takes some time on the clock.
                for (writesDone = 0; writesDone < writesDesired; ++writesDone)
                    using (new CodeTimer("WriteAsync with its await"))
                        strm.Seek(0, SeekOrigin.Begin);
                        // The next two lines could be combined as
                        // await strm.WriteAsync(bytes, 0, bytes.Length);
                        var task = strm.WriteAsync(bytes, 0, bytes.Length);.
                        await task;


Now here are the results.

Based on this output, we can observe the following.

  1. Execution proceeds as it normally would until we reach the await on line 37 for the first time.
  2. At that moment, the asyncrhonous write on the previous line has not completed, so we leave the WriteFileAsync method, returning a Task for the rest of it, and bop up to line 13. That's when the output says, "Calling WriteFileAsync: Finished in 0.0061 seconds."
  3. It's important to realize that the rest of WriteFileAsync does not continue executing. The async/await mechanism is single-threaded! WriteFileAsync is totally paused.
  4. Proceeding from line 13, just two lines later, our single thread finds that it wants to await the completion of WriteFileAsync's task. This is where the output says, "Awaiting WriteFileAsync: Starting." At that point, the single thread switches back to finishing WriteFileAsync.
  5. The remainder of WriteFileAsync's loop is executed. Since the calling method is already waiting for WriteFileAsync to complete, there's no more bopping out of WriteFileAsync. It must complete this time, and it does. ("Executing WriteFileAsync: Finished in 0.1115 seconds.")
  6. The await on line 15 is now satisfied and we print, "Awaiting WriteFileAsync: Finished in 0.0535 seconds."

In the next two posts, we will find two ways that async processing can end up being plain old sequential processing, in spite of your intentions.

Tags: ,


Add comment

About the Author

Larry Spencer

Larry Spencer develops software with the Microsoft .NET Framework for ScerIS, a document-management company in Sudbury, MA.