CallContext.即使在不存在异步的情况下,也可以恢复LogicalGetData.为什么

本文关键字:也可以 恢复 LogicalGetData 为什么 情况下 不存在 异步 CallContext | 更新日期: 2023-09-27 18:02:41

我注意到CallContext.LogicalSetData/LogicalGetData不像我期望的那样工作。即使在没有异步或任何类型的线程切换时,async方法内设置的值也会被恢复

下面是一个简单的例子:

using System;
using System.Runtime.Remoting.Messaging;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication
{
    class Program
    {
        static async Task<int> TestAsync()
        {
            CallContext.LogicalSetData("valueX", "dataX");
            // commented out on purpose
            // await Task.FromResult(0); 
            Console.WriteLine(CallContext.LogicalGetData("valueX"));
            return 42;
        }
        static void Main(string[] args)
        {
            using(ExecutionContext.SuppressFlow())
            {
                CallContext.LogicalSetData("valueX", "dataXX");
                Console.WriteLine(CallContext.LogicalGetData("valueX"));
                Console.WriteLine(TestAsync().Result);
                Console.WriteLine(CallContext.LogicalGetData("valueX"));
            }
        }
    }
}

输出如下:

<>之前dataXXdataX42dataXX之前

如果我使TestAsync非异步,它按预期工作:

static Task<int> TestAsync()
{
    CallContext.LogicalSetData("valueX", "dataX");
    Console.WriteLine(CallContext.LogicalGetData("valueX"));
    return Task.FromResult(42);
}
输出:

<>之前dataXXdataX42dataX之前

如果我在TestAsync中有一些真正的异步,我会理解这种行为,但这里不是这样。我甚至使用ExecutionContext.SuppressFlow,但这并没有改变任何东西。

有人能解释一下为什么它是这样工作的吗?

CallContext.即使在不存在异步的情况下,也可以恢复LogicalGetData.为什么

在这种情况下,"As expected"对于不同的人来说是不同的。:)

在最初的Async CTP(没有修改任何框架代码)中,根本不支持"Async -local"类型的上下文。MS在。net 4.5中修改了LocalCallContext以添加此支持。旧的行为(具有共享的逻辑上下文)在处理异步并发(即Task.WhenAll)时尤其有问题。

我在我的博客中解释了async方法中LocalCallContext的高级机制。关键在这里:

当一个async方法启动时,它通知它的逻辑调用上下文来激活写时复制行为。

在逻辑调用上下文中有一个特殊的写时复制标志,每当async方法开始执行时,该标志就会被翻转。这是由async状态机完成的(具体来说,在当前实现中,AsyncMethodBuilderCore.Start调用ExecutionContext.EstablishCopyOnWriteScope)。"flag"是一种简化-没有实际的布尔成员或任何东西;它只是以一种方式修改状态(ExecutionContextBelongsToCurrentScope和友元),以便将来的任何写入都将(浅层)复制逻辑调用上下文。

同样的状态机方法(Start)在完成async方法的同步部分后将调用ExecutionContextSwitcher.Undo

相关文章: