ConfigureAwait(false)-延续是否始终在不同的线程上运行?

时间:2018-11-24 09:14:26

标签: c# asp.net .net asp.net-web-api async-await

ASP.NET WebApi 2(不是ASP.NET Core)的上下文中询问此问题。我已经尝试过对此主题进行自己的研究,但是我找不到明确的答案。

ConfigureAwait(...)方法参数的官方MSDN documentation指出:

  

true尝试将继续情况编组回捕获的原始上下文;否则为false

Stephen Toub进一步解释了attempt关键字,如下所示:

  

这意味着可能没有什么要整理的……可能没有要捕获的上下文,例如SynchronizationContext.Current可能返回null

如果我对它的理解正确,那么ASP.NET WebApi 2则不是这种情况,因为存在AspNetSynchronizationContext,对吗?

现在让我们看看以下控制器动作方法:

[HttpGet]
public async Task<String> GetValues()
{
    // First half.

    var values = await HeavyIo().ConfigureAwait(false);

    // Second half.

    return values;
}

通过传递continueOnCapturedContext: false是否可以确保标记为// Second half.的延续在其他线程上始终得到始终执行?还是有可能如果在异步操作完成后捕获同步上下文的线程是空闲的,然后延续将在同一线程上运行?

2 个答案:

答案 0 :(得分:4)

当我以类似否定的形式问到答案时,我认为这很清楚-无法保证下半部分将在与上半部分不同的线程上执行。正如您推测的那样,当继续执行时,原始线程很可能是幸运的,即将被选中的可用线程。

还要注意的是,还原的是上下文,不一定是 thread 。在Windows消息循环的情况下(例如WinForms UI线程),运行消息循环的UI线程将拾取并执行继续操作,因此使用ConfigureAwait(true)可以确保使用同一线程。但是,对于其他SynchronizationContext,可能没有特别的理由要求或什至更喜欢原始线程,只要它们认为是“上下文”的内容得以恢复即可。例如HttpContext.Current [,身份,文化]。

HeavyIo()至少有一个理论上的同步机会,在这种情况下,无论如何都不会进行上下文切换,并且后半部分将继续与前半部分在同一线程上继续。我只能从您的命名选择(“繁重”)中假设您暗示这不是一个选择。

答案 1 :(得分:0)

在您的示例中,不保证它会在不同的线程上运行,因为这取决于 HeavyIO 的作用。

如果 HeavyIO 这样做,那么它会保证“后半部分”总是一个不同的线程。

private async Task<string> HeavyIo1()
{
    string test = "";
    await Task.Run(() =>
    {
        //Call some syncronous library thats very slow

    }).ConfigureAwait(false);

    return test;
}

例如,如果HeavyIO 在日期> 2011 年执行以下操作,它将在与“上半年”相同的线程上返回。如果日期在 2011 年之前,它将在不同的线程中返回。

private async Task<string> HeavyIo2()
{

    if (DateTime.Now > new DateTime(2011, 1, 1)) return null;

    string test = "";
    await Task.Run(() =>
    {
        //Call some syncronous library thats very slow
    }).ConfigureAwait(false);

    return test;
}