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
,但这并没有改变任何东西。
有人能解释一下为什么它是这样工作的吗?
在这种情况下,"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
。