【问题标题】:Strange deadlock situation in ASP.NET Core ControllerASP.NET Core Controller 中的奇怪死锁情况
【发布时间】:2018-03-18 07:22:37
【问题描述】:

在我的 ASP.NET Core 应用程序中,我有一个看似非常简单的操作。它等待来自异步方法的某个值,然后将其作为 OK 结果返回:

public async Task<IActionResult> GetNextCommand()
{
    var command = await LongPollManager.Instance.GetNextCommand(HttpContext.RequestAborted);
    return Ok(command);
}

当我使用一些 HTTP 客户端调用此路由时,我可以在调试器中验证此异步方法是否返回所需的值并将其传递给 Ok 方法:

如果我让调试器继续运行,我希望在我的 HTTP 客户端中得到结果。但是客户端永远不会收到响应。

当我中断调试器时,我可以看到线程被某个内部锁阻塞。您可以在当前的屏幕截图中看到这一点:

只有在我对 LongPollManager 类进行了一些更改后才能看到这种行为(这实际上非常复杂,并且在内部使用了 TaskCompletionSources 和 ConcurrentDictionaries,以及 SemaphoreSlims)。

令我困惑的是,实际上阻塞的不是我自己的GetNextCommand 方法,而是阻塞似乎发生在 ASP.NET Core 内部。一旦执行在第 29 行并且我得到了我的 command 对象,我的 LongPollManager 类的所有复杂异步内容都结束了,我看不出我在 LongPollManager 中所做的任何更改如何阻止 ASP.NET Core 正常运行完成请求。

ASP.NET Core 在这里等待什么?我的代码(运行到第 29 行没有死锁)怎么会导致这样的死锁情况?

【问题讨论】:

  • 客户如何拨打电话
  • 客户端是同一台机器上的另一个进程,在该路由上执行 HTTP GET。
  • 返回的模型是 POCO 还是序列化时调用的任何连锁效应。
  • 好点 - 它曾经只包含 strings 和 bools。但随着我的最新更改,它还包含一个公共 Task 属性。当 ASP.NET Core 尝试序列化它时,您认为这会产生任何副作用吗?
  • 会的。因为它会尝试调用属性来获取要序列化的值。因为它是一个任务,很可能会尝试同步序列化Task.Result,这会导致死锁。

标签: c# asp.net-core async-await task-parallel-library deadlock


【解决方案1】:

如 cmets 中所述

但在我最新的更改中,它还包含一个公共 Task 属性。当 ASP.NET Core 尝试序列化它时,您认为这会产生什么副作用吗?

会的。

框架将尝试调用该属性来获取序列化的值。并且由于该属性返回一个Task,很可能会尝试同步序列化任务的.Result 属性,这将导致您的死锁。

.Result.Wait() 这样混合使用异步和阻塞调用会导致死锁,应该避免。

参考Async/Await - Best Practices in Asynchronous Programming

操作应该返回简单的 POCO,在序列化时没有副作用。

公共 Task 属性应该对 serailizer 隐藏,通过属性忽略,或者应该转换为在模型序列化时不会被调用的方法。

【讨论】:

    猜你喜欢
    • 2015-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多