【问题标题】:Deadlock with async/await异步/等待死锁
【发布时间】:2016-07-18 21:31:04
【问题描述】:

假设我正在编写一个自定义 MVC 过滤器,它在方法覆盖中执行一些异步调用,如下所示:

public class MyActionFilter : System.Web.Mvc.ActionFilterAttribute
{
  public override void OnActionExecuted(ActionExecutedContext ctx)
  {
    var stuff = ConfigureAwaitHelper1().Result;
    // do stuff
  }

  public override void OnActionExecuting(ActionExecutingContext ctx)
  {
    var stuff = ConfigureAwaitHelper2().Result;
    // do stuff
  }

  private async Task<string> ConfigureAwaitHelper1()
  {
    var result = await client.GetAsStringAsync("blah.com").ConfigureAwait(false);
    return result;
  }

  private async Task<string> ConfigureAwaitHelper2()
  {
    return await client.GetAsStringAsync("blah.com").ConfigureAwait(false);
  }
}

为什么OnActionExecuting 会死锁,而OnActionExecuted 不会?我看不出两者之间的根本区别。返回的行为只有在异步任务完成之后才会发生,这更像是在返回之前将结果放入“匿名返回”本地变量中,所以我不明白为什么前者会死锁。

【问题讨论】:

  • 您在问“当我以一种方式违反规则时它不起作用,但当我以另一种方式违反规则时它确实有效,为什么我允许我以第二种方式违反规则”,答案是不是您不允许以第二种方式违反规则,您很幸运没有被发现违反规则。我不知道为什么第二个有效,但对我来说这并不重要,因为你不应该阻止它。
  • 另外,为了让.ConfigureAwait(false) 真正让您执行.Result 而不会死锁调用链中的每一个等待,必须在其上设置.ConfigureAwait(false)。如果一个没有,而那个 await 是导致 conintuation 发生的那个,你将陷入僵局。您需要显示 client.GetAsStringAsync 的代码及其下的每个 async/await 调用,直到找到仍然具有上下文数据的调用。
  • 我有什么选择? ActionFilterAttribute 是同步的,我无法控制 client。我知道您一直担心这样做,但让我们假设这个示例是完整的,并且在 client.GetAsStringAsync() 中没有任何进一步的配置。
  • 你能仔细检查一下是OnActionExecuting,而不是OnActionExecuted吗?

标签: c# asp.net-mvc async-await synchronizationcontext


【解决方案1】:

为什么 OnActionExecuting 会死锁,而 OnActionExecuted 不会?

我很惊讶它完全有效。您遇到死锁的原因是您在Task 上调用.Result。这是邪恶的,您应该只在控制台应用程序中调用.Result.Wait

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-12-22
    • 1970-01-01
    • 2021-09-18
    • 2018-08-26
    • 1970-01-01
    相关资源
    最近更新 更多