Async method lacks 'await' operators and will run synchronously...

by Larry Spencer Thursday, October 11, 2012 4:23 AM

To wrap up our discussion of the async/await pattern, I wanted to mention a common error:

This async method lacks 'await' operators and will run syncrhonously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Here's how we could change the code from our sample to produce the error. I have changed line 33 from...

 

var task = strm.WriteAsync(bytes, 0, bytes.Length);
await task;

 

...to the non-Async version: 

 

strm.Write(bytes, 0, bytes.Length);

 

class Program
{
    static void Main(string[] args)
    {
        WriteALot();
        Console.ReadLine();
    }

    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))
            {
                for (writesDone = 0; writesDone < writesDesired; ++writesDone)
                {
                    using (new CodeTimer("WriteAsync with its await"))
                    {
                        strm.Seek(0, SeekOrigin.Begin);
                        strm.Write(bytes, 0, bytes.Length);
                    }
                }
            }
        }
    }
}

The result is synchronous execution:

You'd think that the Write call would be the perfect moment for the program to take advantage of multi-threading and go back and do some work in the calling routine, right? Yes, that's right ... until we realize once again that async/await is single-threaded. The only time it does something that looks sort of like multi-threading is when you call one of the special Async methods in the .NET API or when you await a Task.

The first post in this series has an example of calling an Async API method:

 

var task = strm.WriteAsync(bytes, 0, bytes.Length);
await task;

Equivalently, you could do this:

 

await strm.WriteAsync(bytes, 0, bytes.Length);

 

The Task.Run strategy would look like this. (Normally, you'd only use this for CPU-bound operations. It's better to use an Async API method if one is available.)

 

await Task.Run(() => strm.Write(bytes, 0, bytes.Length));

Any of the above will produce output similar to this, showing that execution passed back to the caller upon the await, as explained in the previous post.

Tags: ,

All | General

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.