ASP.NET 死锁尽管 ConfigureAwait(false)

ASP.NET deadlock despite ConfigureAwait(false)

编辑:我现在已经通过简单地将按钮单击处理程序标记为异步无效并等待任务来解决了这个问题。我认为 WebForms 无法以任何方式处理异步,除非与 RegisterAsyncTask 一起使用。虽然这解决了我的问题,但我仍然对为什么下面的代码会死锁感兴趣,因为它违背了我目前对异步 C# 代码如何工作的理解,所以仍然感谢答案。

我有一个服务公开了一个向某些 api 发送请求的异步方法。此方法在 Web 表单代码隐藏中使用。我知道 ASP.NET 一次只允许一个线程执行,因此调用 Task.Wait() 会导致死锁,因为由于上下文线程被阻塞,等待的任务在完成后无法恢复执行。

然而,据我了解(通过阅读 this blog),在等待的任务上调用 ConfigureAwait(false) 会导致任务改为在线程池线程上 运行,因此执行上下文线程可以恢复。不过,我仍然从下面的代码中遇到僵局。这是为什么?

protected void Activate(object sender, CommandEventArgs e)
{
    var someID = int.Parse((string) e.CommandArgument);
    DoAsyncThingWithID(someID).Wait();
}

private async Task DoAsyncThingWithID(int ID)
{
    try
    {
        await new SomeService()
            .DoSomeAsyncWork(ID)
            .ConfigureAwait(false);
    }
    catch (AppropriateException e)
    {
        DealWithIt();
    }
}

可能值得注意:DoSomeAsyncWork(int) 有更多的异步方法"under"。在底部有一个 api 包装对象(负责发送 HTTP 请求),其方法不是异步的,但使用 Task.Run(() => api.SendThingy());

调用

这可能是问题所在吗?

我现在已经解决了这个问题,只需将按钮单击处理程序标记为 async void 并等待任务即可。我认为 WebForms 无法以任何方式处理 async,除非与 RegisterAsyncTask 一起使用。虽然这解决了我的问题,但我仍然对为什么原始代码会死锁感兴趣,因为它违背了我目前对异步 C# 代码如何工作的理解,因此仍然感谢进一步的答案。

It is however my understanding (from reading this blog) that calling ConfigureAwait(false) on the awaited task causes the task to run on a thread pool thread instead, and therefore execution on the context thread can resume.

实际上,使用 ConfigureAwait(false) 意味着 当前方法的延续 不关心它在什么上下文中执行 - 绝大多数时间这意味着它将在线程池线程上继续

因此,DoSomeAsyncWork 在 ASP.NET 上下文中仍将 运行。因此,仅一个 ConfigureAwait(false) 是不够的。您必须确保 DoSomeAsyncWork 也使用 ConfigureAwait(false),以及它调用的所有异步方法,以及它们调用的所有异步方法等,包括 Microsoft 或第三方库方法。

这就是为什么我建议一开始不要阻止。 ConfigureAwait(false) hack 只是尝试绕过它的方法之一,如果你绝对 的话。