尽管ConfigureAwait(false)

时间:2015-11-20 08:57:41

标签: c# asp.net async-await task deadlock

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

我有一个服务公开异步方法,该方法向某些api发送请求。此方法在webforms代码隐藏中使用。我知道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)在其下面有更多的异步方法。在底部有一个api包装对象(负责发送HTTP请求),其方法不是异步,但是使用Task.Run(() => api.SendThingy());

调用

这可能是问题吗?

2 个答案:

答案 0 :(得分:5)

  

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

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

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

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

答案 1 :(得分:1)

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