使用async/await出现死锁问题

3
假设我正在编写一个自定义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 却不会?我看不到两者之间的根本区别。返回操作只会在异步任务完成后进行,这有点像将结果放入“匿名返回”局部变量中再返回它,所以我不明白为什么前者会死锁。

5
你在问“当我以某种方式违反规则时,一种方式行不通,但另一种方式却有效,为什么我可以用第二种方式违反规则”,答案是你不被允许用第二种方式违反规则,只是幸运地没有被发现。我不知道为什么第二种方式有效,但对我来说这并不重要,因为无论如何你都不应该去阻止它。 - Scott Chamberlain
2
此外,对于 .ConfigureAwait(false),如果要实际上让你在不死锁的情况下执行 .Result,则必须在其上设置 .ConfigureAwait(false)。如果单个 await 没有设置并且该 await 是导致继续发生的 await,则会发生死锁。您需要展示 client.GetAsStringAsync 的代码以及其下的每个 async/await 调用,直到找到仍具有上下文数据的调用。 - Scott Chamberlain
我有什么替代方案?ActionFilterAttribute是同步的,而且我无法控制client。我知道你担心一路做下去,但让我们假设这个示例已经完成了,并且在client.GetAsStringAsync()内部没有进一步配置的内容。 - gzak
你能再确认一下是 OnActionExecuting 死锁了而不是 OnActionExecuted 吗? - Diego
我刚刚确认了,在我的机器上都死锁了,但是我的同事进行了一些额外的更改,她从client.GetAsStringAsync()方法中删除了async/await关键字,直接返回原始任务。在她的情况下,她看到了这里描述的行为。 - gzak
1
可能是 Async Await Handler Deadlock 的重复问题。 - Igor
1个回答

1
为什么OnActionExecuting会死锁而OnActionExecuted不会?
我很惊讶它能正常工作。你遇到死锁的原因是因为在Task上调用了.Result。这是有害的,你只应该在控制台应用程序中调用.Result和.Wait。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接