了解多个configureawait(false)在单个异步方法中执行的操作

jon*_*ith 3 .net c# async-await

考虑以下代码:

public async Task SomeMethodAsync(){
    //1. code here executes on the original context

    //for simplicity sake, this doesn't complete instantly 
    var result1 = await Method1Async().ConfigureAwait(false);

    //2. code here doesn't executes in the original context 
Run Code Online (Sandbox Code Playgroud)

现在我的问题是,如果在上面的方法中有另一个异步方法调用:

    //again for simplicity sake, this doesn't complete instantly 
    var result2 = await Method2();
    //3. code in question is here
}
Run Code Online (Sandbox Code Playgroud)

有问题的代码是在原始上下文中运行还是在另一个上下文中运行(可能是来自线程池或其他上下文的线程)?

此外,如果使用ConfigureAwait(false)调用method2,那么相关代码是否会在与段号2相同的上下文中运行?

Sha*_*tin 8

简化的原始代码

//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
Run Code Online (Sandbox Code Playgroud)

答案

假设你的异步方法没有立即完成,这里有两个简短的答案.

有问题的代码是在原始上下文中运行还是在另一个上下文中运行(可能是来自线程池或其他上下文的线程)?

有问题的代码将具有空SynchronizationContext.Current值,因此将以默认值运行SynchronizationContext.

关于有问题的代码运行的线程:SynchronizationContext做出决定.代码被发布/发送到a SynchronizationContext,后者又将操作转发到特定的计算资源,例如特定的线程,特定的CPU核心或其他东西.在默认情况下SynchronizationContext,线程的选择取决于托管应用程序的内容(例如,控制台应用程序与ASP.NET应用程序).在非默认情况下SynchronizationContext,计算资源的选择取决于实现者的心血来潮:它可以在网络共享上运行.

此外,如果使用ConfigureAwait(false)调用method2,那么相关代码是否会在与段号2相同的上下文中运行?

如果method2有ConfigureAwait(false),那么有问题的代码也将在默认情况下运行SynchronizationContext.换句话说,当我们使用时false,任务不再尝试在捕获的上下文中继续.

实验

这是一个实验(完整列表在这里),可以回答你的两个问题.

该实验使用SynchronizationContext维护简单的a ,将其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;
}
Run Code Online (Sandbox Code Playgroud)

它是什么让我们看到代码是否在原始上下文中运行.

那么,让我们回忆一下你问的问题:

有问题的代码是在原始上下文中运行还是在另一个上下文中运行(可能是来自线程池或其他上下文的线程)?

要回答这个问题,我们有一个接近您的设置的实验.它首先将原始文件设置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);
}
Run Code Online (Sandbox Code Playgroud)

您想知道在第二种方法之后运行的代码是否会在原始上下文中运行.输出答案.第一个和第二个异步方法都没有将它们的延续发布到原始上下文.

上面的代码ConfigureAwait(false)输出:

Before:The Original Context
After Result1:                       
After Result2:               
Run Code Online (Sandbox Code Playgroud)

如果我们改变上面的代码ConfigureAwait(true),两个方法都在原始上下文中运行它们的连续,输出是这样的:

Before:The Original Context        
After Result1:The Original Context   
After Result2:The Original Context
Run Code Online (Sandbox Code Playgroud)

所以你有它.对于我来说,运行带有多种不同值的和的各种组合的完整代码列表以及延迟为0以查看会发生什么是很有启发性的.truefalseSynchronizationContext

还有值得阅读SynchronizationContext的部分内容吗?它的所有有关的SynchronizationContext