I've been reading up a bit on async/await
and was trying to reproduce a deadlock scenario in windows forms by calling WebClient.DownloadStringTaskAsync
on the UI thread and then calling task.Result
on the UI thread while the task was executing. This resulted in a deadlock.
Then I tried to address the issue by calling ConfigureAwait(false)
on the returned task, but to my surprise that still caused a deadlock. My understanding is that it should execute the continuation of the method on a different thread and hence there should be no deadlock. What am I missing?
I know how to get around the issue but thought ConfigureAwait(false)
would fix it as well.
Here is the code, I am using NET 4.5
private async void button1_Click(object sender, EventArgs e)
{
// Deadlocks!
Task<string> task = DownloadAsync();
textBox1.Text = task.Result;
// Works
//textBox1.Text = await DownloadAsync();
}
private static async Task<string> DownloadAsync()
{
var client = new WebClient();
string result = await client.DownloadStringTaskAsync("http://www.rpmglobal.com").ConfigureAwait(false);
return result;
}
Using ConfigureAwait(false) to avoid deadlocks is a dangerous practice. You would have to use ConfigureAwait(false) for every await in the transitive closure of all methods called by the blocking code, including all third- and second-party code. Using ConfigureAwait(false) to avoid deadlock is at best just a hack).
If some user code (or other library code your app is using) sets a custom context and calls your code, or invokes your code in a Task scheduled to a custom TaskScheduler, then even in ASP.NET Core your awaits may see a non-default context or scheduler that would lead you to want to use ConfigureAwait(false).
The deadlock occurs because the await is waiting for the UI thread to be free and the UI thread is blocked on the async method to complete. ConfigureAwait (false) avoids the deadlock by allowing the await to resume on a thread pool thread instead of the UI thread.
Avoiding deadlocks. Consider a library method that uses await on the result of some network download. You invoke this method and synchronously block waiting for it to complete, such as by using .Wait() or .Result or .GetAwaiter().GetResult() off of the returned Task object.
WebClient
will still cause this deadlock because DownloadStringTaskAsync
is just a wrapper around the EAP methods, which always raise their event in the original context. There's no way to turn this off.
Try using HttpClient
(or something simple like Task.Delay
) to see the difference.
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