【问题标题】:ASP.NET Web API async controller method and deadlockASP.NET Web API 异步控制器方法和死锁
【发布时间】:2019-09-03 10:21:33
【问题描述】:

请帮助我理解为什么这段代码会导致死锁? 我有一个 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() 中?强制同步代码异步运行是没有意义的,它只会使用更多线程。如果你不能一直异步,坚持同步。
  • 这是代码的一部分,我需要并行运行一些操作@MarcusHöglund
  • 我没有看到任何东西会在您的代码中并行运行。记住:异步!= 并行
  • “这是代码的一部分”的意思是:我们发现死锁的机会很小。
  • otoh,没有那个(和类似的)Debug.WriteLine() 行会死机吗?

标签: c# asp.net .net async-await deadlock


【解决方案1】:

ConfigureAwait(false) 在这里对您没有帮助,因为它必须一直在调用堆栈中(请参阅更多 here)而不是在您同步等待的地方,即它取决于 base.SendAsync 的实现.如果它在当前线程上获得了锁,那么就为时已晚。毕竟也不建议在 ASP.net 管道中继续响应其他线程(参见讨论 here 和帖子 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;
    }
}

【讨论】:

    【解决方案2】:

    我认为您忽略了 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;
        }
    

    【讨论】:

      猜你喜欢
      • 2017-11-23
      • 2017-05-19
      • 2020-11-26
      • 1970-01-01
      • 2013-04-22
      • 2012-08-08
      • 1970-01-01
      • 2018-06-26
      • 1970-01-01
      相关资源
      最近更新 更多