I recently stumbled over a deadlock in our code. Someone called a method, that returned a Task, and instead of awaiting it, he called:
var res = Method().Result;
I reason for this code was just plain laziness, because using await requires an async-context, which probably isn’t that easy to achieve. But what’s the reason for that deadlock, and why is awaiting the result actually resolving it?
I don’t want to dick very deeply into the Task Parallel Library (TPL). But let’s just make clear, what a task is. A task is a “unit of execution”, it has no direct link to Threads (even though it’s in the same namespace). A Thread can run tasks, there can be one or more Threads in your program. Let’s assume that Threads run in parallel.
So now let’s make up some setting, we call a method (which returns and starts a Task, that’s what “async Task” does) and then wait for the result by calling “.Result”. That call will actually block the current thread and wait for the second task to finish.
When we look at the graphic above, we see that this call is working. We actually block Thread 1, but we have not created a deadlock. But we can never be sure, that the TaskScheduler (it is planning what Tasks runs on which Thread) really runs Task2 on Thread2. As soon as the TaskScheduler decides to run Task2 on Thread1, we have a classic deadlock:
But what is the difference to async/await pattern?
A couple of years ago, a friend described async-await pattern as syntactical sugar, it’s not a language feature, nor a real pattern. There is something true to that, but it makes programming Tasks so much easier. It actually splits your code into separate parts, and calls them in a sequence. So there will be no busy wait, no matter which Thread the Task we are waiting for is executed on.