此问题是在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;
}