I’ve been considering the new async stuff in C# 5, and one particular question came up.
I understand that the await keyword is a neat compiler trick/syntactic sugar to implement continuation passing, where the remainder of the method is broken up into Task objects and queued-up to be run in order, but where control is returned to the calling method.
My problem is that I’ve heard that currently this is all on a single thread. Does this mean that this async stuff is really just a way of turning continuation code into Task objects and then calling Application.DoEvents() after each task completes before starting the next one?
Or am I missing something? (This part of the question is rhetorical – I’m fully aware I’m missing something 🙂 )
It is concurrent, in the sense that many outstanding asychronous operations may be in progress at any time. It may or may not be multithreaded.
By default,
awaitwill schedule the continuation back to the “current execution context”. The “current execution context” is defined asSynchronizationContext.Currentif it is non-null, orTaskScheduler.Currentif there’s noSynchronizationContext.You can override this default behavior by calling
ConfigureAwaitand passingfalsefor thecontinueOnCapturedContextparameter. In that case, the continuation will not be scheduled back to that execution context. This usually means it will be run on a threadpool thread.Unless you’re writing library code, the default behavior is exactly what’s desired. WinForms, WPF, and Silverlight (i.e., all the UI frameworks) supply a
SynchronizationContext, so the continuation executes on the UI thread (and can safely access UI objects). ASP.NET also supplies aSynchronizationContextthat ensures the continuation executes in the correct request context.Other threads (including threadpool threads,
Thread, andBackgroundWorker) do not supply aSynchronizationContext. So Console apps and Win32 services by default do not have aSynchronizationContextat all. In this situation, continuations execute on threadpool threads. This is why Console app demos usingawait/asyncinclude a call toConsole.ReadLine/ReadKeyor do a blockingWaiton aTask.If you find yourself needing a
SynchronizationContext, you can useAsyncContextfrom my Nito.AsyncEx library; it basically just provides anasync-compatible “main loop” with aSynchronizationContext. I find it useful for Console appsand unit tests(VS2012 now has built-in support forasync Taskunit tests).For more information about
SynchronizationContext, see my Feb MSDN article.At no time is
DoEventsor an equivalent called; rather, control flow returns all the way out, and the continuation (the rest of the function) is scheduled to be run later. This is a much cleaner solution because it doesn’t cause reentrancy issues like you would have ifDoEventswas used.