First a small bit of background information. I am in the process of making existing C# library code suitable for execution on WinRT. As a minor part of this code deep down needs to do a little file IO, we first tried to keep things synchronous and used Task.Wait() to stop the main thread until all IO was done.
Sure enough, we quickly found out that leads to a deadlock.
I then found myself changing a lot of code in a prototype to make it "asynchronous". That is, I was inserting async and await keywords, and I was changing the method return types accordingly. This was a lot of work - too much senseless work in fact -, but I got the prototype working this way.
Then I did an experiment, and I ran the original code with the Wait statement on a separate thread:
System.Threading.Tasks.Task.Run(()=> Draw(..., cancellationToken)
No deadlock!
Now I am seriously confused, because I thought that I understood how async programming works. Our code does not (yet) use ConfigureAwait(false) at all. So all await statements should continue in the same context as they got invoked in. Right? I assumed that that means: the same thread. Now if this thread has invoked "Wait", this should also lead to a deadlock. But it does not.
Do any of you have a clear rock-solid explanation?
The answer to this will determine whether I will really go through messing up our code by inserting a lot of conditional async/await keywords, or whether I will keep it clean and just use a thread that does a Wait() here and there. If the continuations get run by an arbitrary non-blocked thread, things should be fine. However, if they get run by the UI thread, we may be in trouble if the continuation is computationally expensive.
I hope that the issue is clear. If not, please let me know.
I have an async
/await
intro on my blog, where I explain exactly what the context is:
It is SynchronizationContext.Current
, unless it is null
, in which case it is TaskScheduler.Current
. Note: if there is no current TaskScheduler
, then TaskScheduler.Current
is the same as TaskScheduler.Default
, which is the thread pool task scheduler.
In today's code, it usually just comes down to whether or not you have a SynchronizationContext
; task schedulers aren't used a whole lot today (but will probably become more common in the future). I have an article on SynchronizationContext
that describes how it works and some of the implementations provided by .NET.
WinRT and other UI frameworks (WinForms, WPF, Silverlight) all provide a SynchronizationContext
for their main UI thread. This context represents just the single thread, so if you mix blocking and asynchronous code, you can quickly encounter deadlocks. I describe why this happens in more detail in a blog post, but in summary the reason why it deadlocks is because the async
method is attempting to re-enter its SynchronizationContext
(in this case, resume executing on the UI thread), but the UI thread is blocked waiting for that async
method to complete.
The thread pool does not have a SynchronizationContext
(or TaskScheduler
, normally). So if you are executing on a thread pool thread and block on asynchronous code, it will not deadlock. This is because the context captured is the thread pool context (which is not tied to a particular thread), so the async
method can re-enter its context (by just running on a thread pool thread) while another thread pool thread is blocked waiting for it to complete.
The answer to this will determine whether I will really go through messing up our code by inserting a lot of conditional async/await keywords, or whether I will keep it clean and just use a thread that does a Wait() here and there.
If your code is async
all the way, it shouldn't look messy at all. I'm not sure what you mean by "conditional"; I would just make it all async
. await
has a "fast path" implementation that makes it synchronous if the operation has already completed.
Blocking on the asynchronous code using a background thread is possible, but it has some caveats:
await Task.Run(..)
, not Task.Run(..).Wait()
). This is particularly true for WinRT apps.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