在我的 ASP.NET Core 应用程序中,我有一个看似非常简单的操作。它等待来自异步方法的某个值,然后将其作为 OK 结果返回:
public async Task<IActionResult> GetNextCommand()
{
var command = await LongPollManager.Instance.GetNextCommand(HttpContext.RequestAborted);
return Ok(command);
}
当我使用某些 HTTP 客户端调用此路由时,我可以在调试器中验证此异步方法是否返回所需的值并将其传递给 Ok 方法:
如果我让调试器继续,我希望在我的 HTTP 客户端中得到结果。但客户端从未收到响应。
当我中断调试器时,我可以看到线程被某些内部锁阻塞。您可以在当前屏幕截图中看到这一点:
仅当我在 LongPollManager
中进行了一些更改后,才能看到此行为。类(实际上非常复杂,内部使用 TaskCompletionSource
和 ConcurrentDictionarie
以及 SemaphoreSlim
)。
令我困惑的是,它实际上不是我自己的GetNextCommand
方法是阻塞的,但阻塞似乎发生在 ASP.NET Core 内部。一旦执行到第 29 行,我就得到了 command
对象,我的 LongPollManager
中所有复杂的异步内容类(class)结束了,我没有看到 LongPollManager
有什么改变可以阻止 ASP.NET Core 正确完成请求。
ASP.NET Core 在这里等待什么呢?我的代码(运行到第 29 行没有死锁)怎么会导致这样的死锁情况?
最佳答案
正如评论中提到的
But with my latest changes it also contains a public
Task
property. Do you think this could have any side-effects when ASP.NET Core tries to serialize it?
是的,会的。
框架将尝试调用该属性来获取序列化的值。当该属性返回一个 Task
时,很可能会尝试同步序列化该任务的 .Result
属性,这会导致死锁。
混合使用异步和阻塞调用(例如 .Result
和 .Wait()
)可能会导致死锁,应该避免。
引用Async/Await - Best Practices in Asynchronous Programming
操作应该返回简单的 POCO,序列化时没有副作用。
公共(public) Task
属性应该对序列化器隐藏,通过属性忽略,或者应该转换为模型序列化时不调用的方法。
关于c# - ASP.NET Core Controller 中奇怪的死锁情况,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46606539/