即使在Asp.Net流中使用ConfigureAwait(false)后死锁也是如此
即使在使用ConfigureAwait(false)
后我也遇到了死锁,下面是示例代码。
根据示例http://blog.stephencleary.com/2012/02/async-and-await.html(#Avoding Context),这不应该达到死锁。
这是我的class级 :
public class ProjectsRetriever { public string GetProjects() { ... var projects = this.GetProjects(uri).Result; ... ... } private async Task<IEnumerable> GetProjects(Uri uri) { return await this.projectSystem.GetProjects(uri, Constants.UserName).ConfigureAwait(false); } }
该类来自共享库:
public class ProjectSystem { public async Task<IEnumerable> GetProjects(Uri uri, string userName) { var projectClient = this.GetHttpClient(uri); var projects = await projectClient.GetProjects(); // code here is never hit ... }
如果我在共享库中添加ConfigureAwait(false)以等待调用,则会起作用,其中进行HttpClient调用:
public class ProjectSystem { public async Task<IEnumerable> GetProjects(Uri uri, string userName) { var projectClient = this.GetHttpClient(uri); var projects = await projectClient.GetProjects().ConfigureAwait(false); // no deadlock, resumes in a new thread. ... }
我一直在浏览所有发现的博客,但我发现只有在与httpClient.AsyncApi()调用一起使用时才会发现ConfigureAwait(false)有效!
请帮忙澄清!!!
来自评论:
我假设,一旦使用了ConfigureAwait(false)(调用堆栈中的任何位置),从该点开始执行不会导致死锁。
我不相信黑魔法,你也不应该。 始终努力了解在代码中使用某些内容时会发生什么。
当您await
返回Task
或Task
的异步方法时, Task.GetAwaiter
方法生成的TaskAwaitable
会隐式捕获SynchronizationContext
。
一旦该同步上下文到位并且异步方法调用完成, TaskAwaitable
尝试将继续(基本上是第一个await
关键字之后的其余方法调用) TaskAwaitable
送到SynchronizationContext
(使用SynchronizationContext.Post
)之前捕获。 如果调用线程被阻塞,等待相同的方法完成,则会出现死锁 。
您应该问自己我应该为异步方法公开同步包装器吗? 99%的时间答案是否定的 。 您应该使用同步API,例如WebClient
提供的API。
它在ProjectsRetriever
使用时会阻塞,因为:
public class ProjectsRetriever { public string GetProjects() { //querying the result blocks the thread and wait for result. var projects = this.GetProjects(uri).Result; ... //require Thread1 to continue. ... } private async Task> GetProjects(Uri uri) { //any thread can continue the method to return result because we use ConfigureAwait(false) return await this.projectSystem.GetProjects(uri, Constants.UserName).ConfigureAwait(false); } } public class ProjectSystem { public async Task> GetProjects(Uri uri, string userName) { var projectClient = this.GetHttpClient(uri); var projects = await projectClient.GetProjects(); // code here is never hit because it requires Thread1 to continue its execution // but Thread1 is blocked in var projects = this.GetProjects(uri).Result; ... }
它在ProjectSystem
使用时不会阻塞,因为:
public class ProjectsRetriever { public string GetProjects() { ... var projects = this.GetProjects(uri).Result; ...//requires Thread1 to continue ... } private async Task> GetProjects(Uri uri) { //requires Thread1 to continue return await this.projectSystem.GetProjects(uri, Constants.UserName); } } public class ProjectSystem { public async Task> GetProjects(Uri uri, string userName) { var projectClient = this.GetHttpClient(uri); var projects = await projectClient.GetProjects().ConfigureAwait(false); // no deadlock, resumes in a new thread. After this function returns, Thread1 could continue to run }
我有同样的问题。 “ConfigureAwait(false)”不能总是避免死锁。
public class HomeController : Controller { public async Task Index() { // This works ! ViewBag.Title = GetAsync().Result; // This cause deadlock even with "ConfigureAwait(false)" ! ViewBag.Title = PingAsync().Result; return View(); } public async Task GetAsync() { var uri = new Uri("http://www.google.com"); return await new HttpClient().GetStringAsync(uri).ConfigureAwait(false); } public async Task PingAsync() { var pingResult = await new Ping().SendPingAsync("www.google.com", 3).ConfigureAwait(false); return pingResult.RoundtripTime.ToString(); } }
对于上面的代码,“GetAsync()”有效,而“PingAsync()”则无效。
但我发现,如果我将异步调用包装到一个新任务中,并等待此任务,PingAsync()将在没有“ConfigureAwait(false)”的情况下运行事件:
var task = Task.Run(() => PingAsync()); task.Wait(); ViewBag.Title = task.Result;
我不知道原因,也许有人可以告诉我差异。