Give me some time

There’s a been a lot of buzz around about the new async construct for C# 5. This is explained quite well in the Channel9 interview with Mads Torgersen with two other really good presentations on the background material from Stephen Toub and Lucian Wischik.

To me the key idea is that this is all about allowing a thread to be shared while the initial method is waiting for something else to finish. This contrasts with other asynchrony mechanisms where the emphasis is to get a threadpool serving lots of requests efficiently. With the async construct there is an well known use-case of allowing the GUI thread to be used for what used to relegated to background processing. The old way of using a BackgroundWorker introduced difficulties with synchronising the background and the foreground processing.

Moreover, the async modifier allows the compiler to take on the work of generating continuations. Instead of requiring this to be expressed at the user program level, making the code hard to read, we get code that is far clearer. We’ve seen this kind of idea before with the introduction of iterators into C# 2. The co-routine, required of an iterator implementation, can be automatically generated by the compiler, instead of requiring the user to implement the state machine.

As in the iterator case, there is a pattern behind the implementation of a user object that can be await’ed. Adding the async modifier to a method, which must then return a Task or void, allows that method to use the await keyword. This await is implemented along the lines of yield return in C# (though it can be used in expressions too). The compiler generates code which records the state point at which the await occurs, and the compiler subscribes a delegate which will be notified when the next state transition needs to be made. This latter stage is handled by the await’ed object having to support the GetWaiter/BeginWait/EndWait pattern.

In the following example, where we’d use a top level call of the form

Task<int> worker = DoWork();
int result = worker.Result;

the DoWork method will process each word using a worker object and it will wait for the result using await. The use of await will allow the processor to exit from the DoWork method, and the generated code will have registered a callback using the BeginWait. This callback will transition the state machine implemented inside the DoWork method allowing the next call to be made. The code inside the DoWork method looks fairly natural without the need for the user to explicitly write the continuations.

static async Task<int> DoWork()
{
    int currentLength = 0;
    foreach(string word in new [] { “It”, “was”, “the”, “best”, “of”, “times” })
    {
        Console.WriteLine(“Processing ‘{0}’ on thread {1}”,
            word, Thread.CurrentThread.ManagedThreadId);
        currentLength += await new GetLength(word);
        if (currentLength > 10)
            return currentLength;
    }
    return -1;
}

We write the GetLength code to implement the GetWaiter pattern, and pretend that the actual computation takes some time to execute by using a Thread.Sleep.

class GetLength
{
    readonly string m_Input;

    public GetLength(string input)
    {
        m_Input = input;
    }

    public MyAwaiter GetAwaiter()
    {
        return new MyAwaiter(m_Input);
    }

    internal class MyAwaiter
    {
        readonly int m_Result;

        public MyAwaiter(string input)
        {
            m_Result = input.Length;
        }

        public bool BeginAwait(Action resumption)
        {
            ThreadPool.QueueUserWorkItem(_ =>
                {
                    Thread.Sleep(2000);
                    resumption();
                });
            return true;
        }

        public int EndAwait()
        {
            return m_Result;
        }
    }
}

There are several things to mention. The BeginWait can return false to say that the task has already finished, allowing a potential efficiency gain. Also, the above code makes no use of SynchronizationContexts which are what should be used to guarantee the execution context of subsequent code. In this example, the first part of the code will happen on the main thread, and the rest will happen on various thread pool threads. In a real implementation we would also have to worry about catching exceptions and re-raising them in the EndAwait.

Generally the code that is await’ed is going to have generated a Task instead of a user object. This class has appropriate extensions method defined on it in the CTP, which support the pattern. We can run the following code to demonstrate this.

Task task = new Task(() => Console.WriteLine(“Bang”));
var awaiterObject = task.GetAwaiter();
awaiterObject.BeginAwait(() => Console.WriteLine(“Resume”));
task.Start();
task.Wait();

In the past, people such Jeff Richter and the CCR team have used the iterator pattern to try to get natural looking code which can give up the thread when it is waiting. It’s nice to see that with a simple language extension we can get the benefits in the language itself, just by adding one modifier and a new keyword, and in a way that allows user classes to extend the facility.

Advertisements
This entry was posted in Computers and Internet. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s