ASP.NET Web API 异步控制器方法和死锁

ASP.NET Web API async controller method and deadlock

请帮我理解为什么这段代码会导致死锁? 我有一个 asp.net web api 应用程序,我试图使一些控制器方法异步。


    [HttpPost]
    [Authentication]
    public async Task<SomeDTO> PostSomething([FromBody] SomeDTO someDTO)
    {
        return await _service.DoSomething(someDTO);
    }

这是被调用的服务方法的样子:


    public async Task<SomeDTO> DoSomething(SomeDTO someDTO)
    {
...
        var someTask = Task.Run(() => 
        {
            var entity = new SomeEntity(someDTO);
            return _repository.Create(entity);
        });
...
        var result = await someTask;
...
    }

还有一些全局处理程序,可以将响应打印到控制台。


    public class AppGlobalHandler : DelegatingHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            var resp = base.SendAsync(request, cancellationToken);
            Debug.WriteLine($"Response:{request.RequestUri}{Environment.NewLine}{resp?.ConfigureAwait(false).GetAwaiter().GetResult()?.Content?.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult()}");
            return resp;
        }
    }

看起来像 ConfigureAwait(false).GetAwaiter().GetResult() 阻塞调用者线程,但我认为 ConfigureAwait(false) 应该避免这种情况,不是吗?

我认为你忽略了 Task.Run() 方法中的 async 关键字。

    public async Task<SomeDTO> DoSomething(SomeDTO someDTO)
    { 
        var someTask = Task.Run( async () => //simply add this for async run 
        {
            var entity = new SomeEntity(someDTO);
            return _repository.Create(entity);
        }); 
        var result = await someTask;
    }

ConfigureAwait(false) 不会在这里帮助你,因为它必须一直在调用堆栈中(查看更多 ) not at place where you wait synchronously, i.e. it depends rather on the implementation of base.SendAsync. If it acquired a lock on current thread it's too late to do something about it. It is also not recommended in ASP.net pipeline to continue responding on other thread after all (see discussion here and post here)。

最后,在异步上下文中同步等待总是一个高风险的想法。 如果您需要阅读内容,为什么不这样做:

 public class AppGlobalHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var resp = await base.SendAsync(request, cancellationToken);
        var content = resp?.Content != null 
           ? (await resp.Content.ReadAsStringAsync()) 
           : string.Empty; 
        Debug.WriteLine($"Response:{request.RequestUri}{Environment.NewLine}{content}");
        return resp;
    }
}