即使在使用 ConfigureAwait(false)
之后我也遇到了死锁,下面是示例代码 .
根据示例http://blog.stephencleary.com/2012/02/async-and-await.html(#Avoding Context),这不应该是命中死锁 .
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.
...
}
我一直在浏览所有发现的博客,但我发现只有在与httpClient.AsyncApi()调用一起使用时,才会发现ConfigureAwait(false)有效!
Please help clarify!!!
3 回答
来自评论:
我不相信黑魔法,你也不应该 . 始终努力了解在代码中使用某些内容时会发生什么 .
当
await
返回Task
或Task<T>
的异步方法时,Task.GetAwaiter
由Task.GetAwaiter
方法生成SynchronizationContext
的隐式捕获 .一旦该同步上下文到位并且异步方法调用完成,
TaskAwaitable
会尝试将继续(基本上是第一个await
关键字之后的其余方法调用)编组到之前捕获的SynchronizationContext
(使用SynchronizationContext.Post
)上 . 如果调用线程被阻塞,等待相同的方法完成,你有一个 deadlock .你应该问自己Should I expose synchronous wrappers for asynchronous methods?百分之99的答案是 no . 您应该使用同步API,例如一个
WebClient
商品 .它在
ProjectsRetriever
中使用时会阻塞,因为:它在
ProjectSystem
中使用时不会阻止,因为:我有同样的问题 . “ConfigureAwait(false)”不能总是避免死锁 .
对于上面的代码,“GetAsync()”有效,而“PingAsync()”则无效 .
但我发现如果我将异步调用包装到一个新任务中,并等待这个任务,PingAsync()将在没有“ConfigureAwait(false)”的情况下运行事件:
我不知道原因,也许有人可以告诉我区别 .