c# - ASP.NET Core Controller 中奇怪的死锁情况

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

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

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

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

enter image description here

如果我让调试器继续,我希望在我的 HTTP 客户端中得到结果。但客户端从未收到响应。

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

enter image description here

仅当我在 LongPollManager 中进行了一些更改后,才能看到此行为。类(实际上非​​常复杂,内部使用 TaskCompletionSourceConcurrentDictionarie 以及 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/

相关文章:

c# - 为什么 SQL Server 实例不总是出现在枚举中?

c# - 有没有办法在运行时设置#define 的值?

asp.net-core - 如何在asp.net core中更改 `ReflectionIT.Mvc.Paging`包样式

c# - 类库和 .NET Core 类库模板有什么区别?

c# - 这是编写异步方法的正确方法吗?

c# - 使用 C# 将 SQL Server 数据库备份到文本文件(如 .csv)

c# - 如果与 F# 类似物进行比较,为什么 C# 字符串的标题为 "Immutable"?

c# - 如何获得实际的请求执行时间

javascript - JavaScript 和 Python Promise/Awaitable Evaluation 的区别

c# - 我能否在同步和异步 lambda 的类似方法中避免重复代码?