即使在Asp.Net流中使用ConfigureAwait(false)后死锁也是如此

Sur*_*tty 12 c# asp.net deadlock task-parallel-library async-await

即使使用后我也遇到死锁ConfigureAwait(false),下面是示例代码.

根据示例http://blog.stephencleary.com/2012/02/async-and-await.html(#Avoding Context),这不应该是死锁.

这是我的班级:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

该类来自共享库:

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
        ...
}
Run Code Online (Sandbox Code Playgroud)

如果我在共享库中添加ConfigureAwait(false)以等待调用,则会起作用,其中进行HttpClient调用:

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.
        ...
}
Run Code Online (Sandbox Code Playgroud)

我一直在浏览所有发现的博客,但我发现只有在与httpClient.AsyncApi()调用一起使用时,才会发现ConfigureAwait(false)有效!

请帮忙澄清!!!

Yuv*_*kov 16

来自评论:

我假设,一旦使用了ConfigureAwait(false)(调用堆栈中的任何位置),从该点执行不会导致死锁.

我不相信黑魔法,你也不应该.始终努力了解在代码中使用某些内容时会发生什么.

await返回一个异步方法TaskTask<T>,存在的一个隐式捕捉SynchronizationContextTaskAwaitable通过产生Task.GetAwaiter方法.

一旦该同步上下文到位并且异步方法调用完成,就会TaskAwaitable尝试将继续(基本上是第一个await关键字之后的其余方法调用)编组到先前捕获的SynchronizationContext(using SynchronizationContext.Post)上.如果调用线程被阻塞,等待相同的方法完成,则会出现死锁.

您应该问自己我应该为异步方法公开同步包装器吗?99%的时间答案是否定的.您应该使用同步API,例如WebClient提供的API .

  • 我创建了一个可以轻松插入您的ASP.NET应用程序的库,以检测这些死锁并帮助您进行跟踪。有关更多信息,请参见https://github.com/ramondeklein/deadlockdetection。 (2认同)

Kha*_* TO 7

它在使用时会阻塞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<IEnumerable<Project>> 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<IEnumerable<Project>> GetProjects(Uri uri, string userName)
    {
        var projectClient = this.GetHttpClient<ProjectHttpClient>(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;
        ...
}
Run Code Online (Sandbox Code Playgroud)

它在使用时不会阻塞ProjectSystem因为:

public class ProjectsRetriever
{
    public string GetProjects()
    {
        ...
        var projects = this.GetProjects(uri).Result;
        ...//requires Thread1 to continue
        ...
    }

    private async Task<IEnumerable<Project>> GetProjects(Uri uri)
    {
        //requires Thread1 to continue
        return await this.projectSystem.GetProjects(uri, Constants.UserName);
    }
}

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. After this function returns, Thread1 could continue to run
}
Run Code Online (Sandbox Code Playgroud)