Let me just post a simple example:
private void MyMethod()
{
Task task = MyAsyncMethod();
task.Wait();
}
private async Task MyAsyncMethod()
{
//Code before await
await MyOtherAsyncMethod();
//Code after await
}
Let's say I run the above code in a single threaded app -like a console app-. I'm having a difficult time understanding how the code //Code after await
would be able to run.
I understand that when I hit the await
keyword in MyAsyncMethod()
control goes back to MyMethod()
, but then I'm locking the thread with task.Wait()
. If the thread is locked, how can //Code after await
ever run if the thread that is supposed to take it is locked?
Does a new thread get created to run //Code after await
? Or does the main thread magically steps out of task.Wait()
to run //Code after await
?
I'm not sure how this is supposed to work?
An await expression in an async method doesn't block the current thread while the awaited task is running. Instead, the expression signs up the rest of the method as a continuation and returns control to the caller of the async method. The async and await keywords don't cause additional threads to be created.
When execution reaches an await expression, the generated code will check whether the thing you're awaiting is already available. If it is, you can use it and keep going. Otherwise, it will add a continuation to the "awaitable" part, and return immediately.
The await keyword, by contrast, is non-blocking, which means the current thread is free to do other things during the wait.
The await operator is used to wait for a Promise . It can only be used inside an async function within regular JavaScript code; however it can be used on its own with JavaScript modules.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With