ASP.NET死锁,尽管ConfigureAwait(false)

本文关键字:false ConfigureAwait 尽管 NET 死锁 ASP | 更新日期: 2023-09-27 18:22:44

EDIT:我现在已经解决了这个问题,只需将按钮单击处理程序标记为async void并等待任务。我认为WebForms不能以任何方式处理异步,除非与RegisterAsyncTask一起使用。虽然这解决了我的问题,但我仍然对下面的代码为什么会死锁感兴趣,因为这违背了我目前对异步C#代码如何工作的理解,所以答案仍然很受欢迎。

我有一个服务公开了一个异步方法,该方法向某个api发送请求。此方法在webforms代码绑定中使用。我知道ASP.NET一次只允许执行一个线程,所以调用Task.Wet()会导致死锁,因为由于上下文线程被阻塞,等待的任务在完成时无法继续执行。

然而,我(通过阅读本博客)了解到,在等待的任务上调用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)"下面"有更多的异步方法。就在底部,有一个api包装对象(负责发送HTTP请求),其方法不是异步的,但使用Task.Run(() => api.SendThingy()); 调用

这可能是问题所在吗?

ASP.NET死锁,尽管ConfigureAwait(false)

然而,我(通过阅读本博客)了解到,在等待的任务上调用ConfigureAwait(false)会导致任务在线程池线程上运行,因此可以继续在上下文线程上执行。

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

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

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

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