Fabian's Mix

Mixins, .NET, and more

C# 5: await and ThreadPool.SwitchTo()

with one comment

Last week, I managed to read the 38-page whitepaper explaining the .NET Task-based Asynchronous Pattern in detail. That’s the pattern behind the new C# and VB.NET “async” support everybody is blogging about, for example, Eric Lippert, Alexandra Rusina, or Jon Skeet.

Those blog posts I’ve linked to give good examples on how you can write code orchestrating asynchronous tasks in a very simple, linear fashion. For example, from Eric Lippert’s blog:

async void ArchiveDocuments(List<Url> urls)
{
Task archive = null;
  for(int i = 0; i < urls.Count; ++i)
  {
    var document = await FetchAsync(urls[i]);
    if (archive != null)
      await archive;
    archive = ArchiveAsync(document);
  }
}

This code starts tasks that asynchronously (“without blocking the application”) fetch documents and archive them, with the coordination between the tasks written using an ordinary for loop and an if clause. This is cool because with previous asynchronous patterns in .NET, ie., the Begin…/End… Asynchronous Programming Model, or the Event-Based Asynchronous Pattern (heavily used by Silverlight applications), coordination of asynchronous tasks is quite cumbersome, and cannot be written in such a linear manner.

With the Task-based Asynchronous Pattern, the term asynchronous does not necessarily mean multithreaded – whether the FetchAsync and ArchiveAsync methods run their work on a background thread, in a separate process, in the cloud, or via some esoteric operating system mechanism does not matter for the orchestration code. The Task-based Asynchronous Pattern uses the .NET synchronization context and task scheduler mechanisms to ensure the coordination method is resumed on the correct thread when an awaited task has finished performing its work; it doesn’t care how (or on which thread) exactly the work was performed.

There is an interesting consequence of this, which I haven’t seen anyone blog about yet: you can also await pseudo-tasks that do not schedule work, but only switch to another context!

Here is an example from the aforementioned whitepaper:

public async void button1_Click(object sender, EventArgs e)
{
  string text = txtInput.Text;

  await ThreadPool.SwitchTo(); // jump to the ThreadPool

  string result = ComputeOutput(text);
  string finalResult = ProcessOutput(result);

  await txtOutput.Dispatcher.SwitchTo(); // jump to the TextBox’s thread
 
  txtOutput.Text = finalResult;
}

I like this a lot! Without await, you would use the ThreadPool.QueueUserWorkItem() method, passing in a continuation delegate that first performs the expensive computations, then calls Dispatcher.BeginInvoke(), passing in a continuation delegate that sets the text box text:

public async void button1_Click(object sender, EventArgs e)
{
  string text = txtInput.Text;

  ThreadPool.QueueUserWorkItem (() => {
      string result = ComputeOutput(text);
      string finalResult = ProcessOutput(result);

      txtOutput.Dispatcher.BeginInvoke ((Action) () => txtOutput.Text = finalResult);
  });
}

Of course, await more or less uses the same continuation concepts under the hoods. But code using await is so much easier to read than explicitly wiring up the delegates.

Asynchrony is one of these situations where syntactic sugar matters a lot, I think.

Written by Fabian

November 8th, 2010 at 9:29 am

Posted in .NET development