ASP.NET 核心控制器中出现奇怪的死锁情况
Strange deadlock situation in ASP.NET Core Controller
在我的ASP.NET核心应用中我有一个看似很简单的动作。它等待来自异步方法的一些值,然后 returns 它作为 OK 结果:
public async Task<IActionResult> GetNextCommand()
{
var command = await LongPollManager.Instance.GetNextCommand(HttpContext.RequestAborted);
return Ok(command);
}
当我使用一些 HTTP 客户端调用此路由时,我可以在调试器中验证此异步方法 returns 所需的值并将其传递给 Ok 方法:
如果让调试器继续运行,我希望在我的 HTTP 客户端中得到结果。但是客户端从未收到响应。
然后当我中断调试器时,我可以看到线程被某个内部锁阻塞。您可以在当前屏幕截图中看到这一点:
这种行为只有在我对 LongPollManager
class 进行了一些更改后才能看到(这实际上非常复杂,并且使用了 TaskCompletionSource
s 和 ConcurrentDictionarie
s,和 SemaphoreSlim
s 内部)。
让我疑惑的是,阻塞的其实不是我自己的GetNextCommand
方法,而是阻塞似乎发生在ASP.NET核心内部。一旦在第 29 行执行并且我得到了我的 command
对象,我的 LongPollManager
class 的所有复杂异步内容都结束了,我看不出我在 [=12] 中有什么改变=] 可以阻止 ASP.NET 核心正确完成请求。
ASP.NET核心在这里等待的究竟是什么?我的代码(运行到第 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?
会的。
框架将尝试调用 属性 来获取序列化的值。由于 属性 return 是 Task
,很可能会尝试同步序列化任务的 .Result
属性,这会导致死锁。
像 .Result
和 .Wait()
这样混合异步和阻塞调用会导致死锁,应该避免。
引用Async/Await - Best Practices in Asynchronous Programming
动作应该 return 序列化时没有副作用的简单 POCO。
public Task
属性 应该从序列化程序中隐藏,通过属性忽略,或者应该转换为在序列化模型时不调用的方法。
在我的ASP.NET核心应用中我有一个看似很简单的动作。它等待来自异步方法的一些值,然后 returns 它作为 OK 结果:
public async Task<IActionResult> GetNextCommand()
{
var command = await LongPollManager.Instance.GetNextCommand(HttpContext.RequestAborted);
return Ok(command);
}
当我使用一些 HTTP 客户端调用此路由时,我可以在调试器中验证此异步方法 returns 所需的值并将其传递给 Ok 方法:
如果让调试器继续运行,我希望在我的 HTTP 客户端中得到结果。但是客户端从未收到响应。
然后当我中断调试器时,我可以看到线程被某个内部锁阻塞。您可以在当前屏幕截图中看到这一点:
这种行为只有在我对 LongPollManager
class 进行了一些更改后才能看到(这实际上非常复杂,并且使用了 TaskCompletionSource
s 和 ConcurrentDictionarie
s,和 SemaphoreSlim
s 内部)。
让我疑惑的是,阻塞的其实不是我自己的GetNextCommand
方法,而是阻塞似乎发生在ASP.NET核心内部。一旦在第 29 行执行并且我得到了我的 command
对象,我的 LongPollManager
class 的所有复杂异步内容都结束了,我看不出我在 [=12] 中有什么改变=] 可以阻止 ASP.NET 核心正确完成请求。
ASP.NET核心在这里等待的究竟是什么?我的代码(运行到第 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?
会的。
框架将尝试调用 属性 来获取序列化的值。由于 属性 return 是 Task
,很可能会尝试同步序列化任务的 .Result
属性,这会导致死锁。
像 .Result
和 .Wait()
这样混合异步和阻塞调用会导致死锁,应该避免。
引用Async/Await - Best Practices in Asynchronous Programming
动作应该 return 序列化时没有副作用的简单 POCO。
public Task
属性 应该从序列化程序中隐藏,通过属性忽略,或者应该转换为在序列化模型时不调用的方法。