简化的原始代码
//for simplicity sake, this doesn't complete instantly
var result1 = await Method1Async().ConfigureAwait(false);
//again for simplicity sake, this doesn't complete instantly
var result2 = await Method2();
//code in question is here
答案
假设您的异步方法不会立即完成,这里有两个简短的答案。
有问题的代码会在原始上下文中运行还是在另一个上下文中运行(可能是线程池中的一个线程或另一个上下文)?
有问题的代码将有一个空的SynchronizationContext.Current 值,因此将在默认的SynchronizationContext 中运行。
关于有问题的代码在哪个线程中运行:SynchronizationContext 做出该决定。代码被发布/发送到SynchronizationContext,后者又将操作转发到特定的计算资源,例如特定的线程、特定的 CPU 内核或其他东西。在默认 SynchronizationContext 的情况下,线程的选择取决于托管应用程序的内容(例如控制台应用程序与 ASP.NET 应用程序)。在非默认 SynchronizationContext 的情况下,计算资源的选择取决于实现者的心血来潮:它可以在网络共享上运行。
此外,如果使用 ConfigureAwait(false) 调用方法 2,相关代码是否会在与段号 2 相同的上下文中运行?
如果method2 有ConfigureAwait(false),那么相关代码也将在默认SynchronizationContext 中运行。换句话说,当我们使用false 时,任务不再尝试在捕获的上下文中继续。
实验
这是一个可以回答您的两个问题的实验 (the full listing is here)。
实验使用SynchronizationContext,它维护一个简单的string State,在Post 期间将自身重置为当前上下文,并覆盖ToString() 以输出其State 值。
public class MySyncContext : SynchronizationContext
{
public string State { get; set; }
public override void Post(SendOrPostCallback callback, object state)
{
base.Post(s => {
SynchronizationContext.SetSynchronizationContext(this);
callback(s);
}, state);
}
public override string ToString() => State;
}
它做了什么让我们看到代码是否在原始上下文中运行。
所以,让我们回忆一下你问的是什么:
有问题的代码会在原始上下文中运行还是在另一个上下文中运行(可能是线程池中的一个线程或另一个上下文)?
为了回答这个问题,我们有一个与您的设置相近的实验。它首先将原始SynchronizationContext 设置为已知状态,然后等待两个异步方法,第一个使用ConfigureAwait(false),同时记录当前的SynchronizationContext。
static async Task Run()
{
var syncContext = new MySyncContext { State = "The Original Context" };
SynchronizationContext.SetSynchronizationContext(syncContext);
Console.WriteLine("Before:" + SynchronizationContext.Current);
await Task.Delay(1000).ConfigureAwait(false);
Console.WriteLine("After Result1:" + SynchronizationContext.Current);
await Task.Delay(1000);
Console.WriteLine("After Result2:" + SynchronizationContext.Current);
}
您想知道在第二种方法之后运行的代码是否会在原始上下文中运行。输出回答了这个问题。第一个和第二个异步方法都不会将它们的延续发布到原始上下文中。
上面带有ConfigureAwait(false) 的代码输出如下:
Before:The Original Context
After Result1:
After Result2:
如果我们把上面的代码改成ConfigureAwait(true),这两个方法都会在原来的上下文中运行它们的延续,输出是这样的:
Before:The Original Context
After Result1:The Original Context
After Result2:The Original Context
所以你有它。运行 the full code listing 与 true 和 false 的各种组合、多个不同的 SynchronizationContext 值以及延迟为 0 的情况对我来说很有启发性。
What does SynchronizationContext do?和It's All About the SynchronizationContext的部分内容也值得一读