I'm hitting deadlock even after using ConfigureAwait(false)
, below is the sample code.
As per the sample http://blog.stephencleary.com/2012/02/async-and-await.html (#Avoding Context), this should not have hit dead lock.
This is my class:
public class ProjectsRetriever
{
public string GetProjects()
{
...
var projects = this.GetProjects(uri).Result;
...
...
}
private async Task<IEnumerable<Project>> GetProjects(Uri uri)
{
return await this.projectSystem.GetProjects(uri, Constants.UserName).ConfigureAwait(false);
}
}
This class is from a shared library:
public class ProjectSystem
{
public async Task<IEnumerable<Project>> GetProjects(Uri uri, string userName)
{
var projectClient = this.GetHttpClient<ProjectHttpClient>(uri);
var projects = await projectClient.GetProjects();
// code here is never hit
...
}
Works if I add ConfigureAwait(false) to await call in shared library, where HttpClient call is made:
public class ProjectSystem
{
public async Task<IEnumerable<Project>> GetProjects(Uri uri, string userName)
{
var projectClient = this.GetHttpClient<ProjectHttpClient>(uri);
var projects = await projectClient.GetProjects().ConfigureAwait(false);
// no deadlock, resumes in a new thread.
...
}
I've been going through all blogs found, only difference I find is ConfigureAwait(false) works when used with httpClient.AsyncApi() call!?
Please help clarify!!!
There are very few use cases for the use of ConfigureAwait(true), it does nothing meaningful actually. In 99% of the cases, you should use ConfigureAwait(false). In . NET Framework by default the Task execution will continue on the captured context, this is ConfigureAwait(true).
As a general rule, every piece of code that is not in a view model and/or that does not need to go back on the main thread should use ConfigureAwait false. This is simple, easy and can improve the performance of an application by freeing the UI thread for a little longer.
ConfigureAwait in ActionIt is what happens when you await a task. You capture the current context before awaiting the task, leaving it to the task context, then recovering (re-entering) it back when the task completes. This process is highly expensive and in many scenarios, you do not need it!
To improve performance and avoid potential deadlocks, use ConfigureAwait(false) in any non-UI code. The exception here is app-level code, such as Windows Forms, WPF, and ASP.NET. ConfigureAwait(true) corresponds to the default behavior and does nothing meaningful, therefore such calls can be safely omitted.
From the comments:
I was under assumption, once ConfigureAwait(false) is used (any where in the call stack), execution from that point will not cause deadlock.
I don't believe in black magic, and neither should you. Always strive to understand what happens when you use something in your code.
When you await
an async method that returns a Task
or a Task<T>
, there is an implicit capture of the SynchronizationContext
by the TaskAwaitable
being generated by the Task.GetAwaiter
method.
Once that sync context is in place and the async method call completes, the TaskAwaitable
attempts to marshal the continuation (which is basically the rest of the method calls after the first await
keyword) onto the SynchronizationContext
(using SynchronizationContext.Post
) which was previously captured. If the calling thread is blocked, waiting on that same method to finish, you have a deadlock.
You should ask yourself Should I expose synchronous wrappers for asynchronous methods? 99 percent of the time the answer is no. You should use a synchronous API, such as the one WebClient
offers.
I had the same problem. "ConfigureAwait(false)" can not always avoid dead lock.
public class HomeController : Controller
{
public async Task<ActionResult> Index()
{
// This works !
ViewBag.Title = GetAsync().Result;
// This cause deadlock even with "ConfigureAwait(false)" !
ViewBag.Title = PingAsync().Result;
return View();
}
public async Task<string> GetAsync()
{
var uri = new Uri("http://www.google.com");
return await new HttpClient().GetStringAsync(uri).ConfigureAwait(false);
}
public async Task<string> PingAsync()
{
var pingResult = await new Ping().SendPingAsync("www.google.com", 3).ConfigureAwait(false);
return pingResult.RoundtripTime.ToString();
}
}
For the above code, "GetAsync()" works while "PingAsync()" doesn't.
But I found that if I wrap the async call into a new task, and wait this task, PingAsync() will work event without "ConfigureAwait(false)":
var task = Task.Run(() => PingAsync());
task.Wait();
ViewBag.Title = task.Result;
I don't know the reason, maybe someone can tell me 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