Let me just post a simple example:
private void MyMethod()
{
Task task = MyAsyncMethod();
task.Wait();
}
private async Task
Code as posted will "Deadlock" in Winform App if called from main thread because you're blocking the main thread with the Wait()
.
But in console app this works. but how?
Answer is hidden in the SynchronizationContext.Current
. await
captures the "SynchronizationContext" and when the task is completed it will continue in the same "SynchronizationContext".
In winform app SynchronizationContext.Current
will be set to WindowsFormsSynchronizationContext
which will post to the call to "Message loop", but who is going to process that? out main thread is waiting in Wait()
.
In console app SynchronizationContext.Current
will not be set by default so it will be null
when no "SynchronizationContext" available for await to capture so it will schedule the continuation to ThreadPool
(TaskScheduler.Default which is ThreadpoolTaskScheduler) and so the code after await works(through threadpool thread).
Aforementioned capturing behavior can be controlled using Task.ConfigureAwait(false);
which will prevent winform app from deadlocking but code after await
no longer runs in UI thread.
Does a new thread get created to run //Code after await?
Maybe. Maybe not. The awaitable pattern implementation for Task
runs the continuation (the bit after the await
expression) using the synchronization context which was "current" when at the start of the await expression. If you're in the context of a UI thread, for example, that means that you'll end up back on the same UI thread. If you're in a thread-pool thread, you'll end up back on some thread-pool thread, but it could be a different one.
Of course with your code sample, if you're in a UI thread, your call to Wait()
will block the UI thread so that the continuation can't run - you need to be careful about that. (Calling Wait()
or Result
on tasks that you don't know to be completed, and which may require work on the current thread, is a bad idea.)
Note that you can call Task.ConfigureAwait so that you can express the intention of not requiring to continue on the same context. This is usually appropriate for library methods which don't care which thread they run on:
await task.ConfigureAwait(false);
(It affects more than the thread - it's the whole context which is captured or not.)
I think it's a good idea to get familiar with what's going on under the hood with await. There's plenty of documentation online, and if you'll allow me a brief plug, there's also the 3rd edition of C# in Depth, and my Tekpub screencast series on the topic. Or start with MSDN and proceed from there.