ConfigureAwait(false)-延续总是在不同的线程上运行吗



此问题是在ASP.NET WebApi 2(而非ASP.NET Core(的上下文中提出的。我试着对这个话题进行自己的研究,但我找不到明确的答案。

ConfigureAwait(...)方法参数的官方MSDN文档说明如下:

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.的延续总是在不同的线程上执行?或者,如果异步操作完成时捕获同步上下文的线程将是空闲的,那么继续操作将在同一线程上运行吗?

当以这样的否定形式被问到时,我认为答案非常清楚——不能保证后半部分将在与前半部分不同的线程上执行。正如您所推测的,当继续执行时,原始线程很可能是下一个幸运的可用线程。

同样需要注意的是,恢复的是上下文,而不一定是thread。在Windows消息循环(例如WinForms UI线程(的情况下,是运行消息循环的UI线程获取并执行延续,因此使用ConfigureAwait(true)可以保证相同的线程。然而,对于其他SynchronizationContext,可能没有特别的理由要求甚至更喜欢原始线程,只要它们认为是"上下文"的东西被恢复即可;例如ASP.NET中的HttpContext.Current[,identity,culture]。

HeavyIo()同步完成至少在理论上也是有可能的,在这种情况下,无论如何都没有上下文切换,后半部分将在与第一部分相同的线程上继续。我只能从你选择的命名("heavy"(中假设,你暗示这不是一个选项。

在您的示例no中,不能保证它会在不同的线程上运行,因为这取决于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,它将在与"1"相同的线程上返回;上半场";。如果日期在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;
}

最新更新