10

I've not had chance to check out the CTP of the new C# async/await feature, but here's something I was wondering:

How does it integrate with the message loop? I assume that in a standard Windows application (Winforms, WPF) the continuations are called by sending messages to the application's message loop, using a Dispatcher or similar?

What if I'm not using a standard windows message loop? For example in a GTK# application or in a console application (if indeed this feature could be of use at all in a console application).

I've searched the internet for information about this but to no avail. Can anyone explain?

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
Grokys
  • 16,228
  • 14
  • 69
  • 101

2 Answers2

6

It uses System.Threading.SynchronizationContext.Current. Both WPF and Winforms install their own version of SynchronizationContext. Which use their message loop to marshal the call from a worker thread back to the main UI thread. Respectively with Dispatcher.Begin/Invoke and Control.Begin/Invoke().

Getting this done in a Console mode app is not easy, its main thread doesn't have a well defined 'idle' state that would allow injecting marshaled method calls in a safe manner that avoids re-entrancy headaches. You could certainly add it but you'll be re-inventing the message loop doing so.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Thanks Hank. Trying to think of a situation where async would be of use in a console application and failing. So I think the lack of a synchronization context in this situation is mostly moot. – Grokys Oct 12 '11 at 22:05
  • 3
    [AsyncEx](http://nitoasyncex.codeplex.com/) includes an [AsyncContext](http://nitoasyncex.codeplex.com/wikipage?title=AsyncContext) class which provides an async-compatible main loop for Console apps (and unit tests). – Stephen Cleary Oct 13 '11 at 03:42
  • More about `SynchronizationContext` [here](http://msdn.microsoft.com/en-us/magazine/gg598924.aspx). – Stephen Cleary Oct 13 '11 at 03:43
4

It all comes down to what the "awaiter" does with the continuation it's passed.

The implementation for Task<T> in the BCL will use the current synchronization context (unless you ask it not to using ConfigureAwait) - which means in WPF/SilverLight it'll use the dispatcher; in Windows Forms it'll use something like Control.BeginInvoke, and in a thread-pool thread it'll just keep run on any thread pool thread. Note that it's your current context at the point of the await expression which is important, as that's what the task will capture for the continuation to run on.

The linked blog post (by Mads Torgersen) does a great job of explaining how it all works under the hood, and I have a series of blog posts which you may find useful too.

John Calsbeek
  • 35,947
  • 7
  • 94
  • 101
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Thanks Jon. In all my excursions in .Net land I've never come across SynchronizationContext.Current, which is what I assume you mean by the "current synchronization context"? Learn something new every day. – Grokys Oct 12 '11 at 22:02
  • 1
    @Groky: Yes. There's more in Stephen Toub's article about performance with async: http://msdn.microsoft.com/en-us/magazine/hh456402.aspx – Jon Skeet Oct 12 '11 at 22:04