C# AsyncLocal的语义与逻辑调用上下文有何不同?
.NET 4.6引入了C# AsyncLocal的语义与逻辑调用上下文有何不同?,c#,.net,async-await,.net-4.6,C#,.net,Async Await,.net 4.6,.NET 4.6引入了AsyncLocal类,用于沿着异步控制流流动环境数据。我以前使用过CallContext.LogicalGet/SetData来实现这一目的,我想知道这两者在语义上是否以及在哪些方面是不同的(除了明显的API差异,比如强类型和不依赖字符串键) 我想知道这两个词在语义上是否不同,以及在哪些方面不同 可以看出,CallContext和asynchlocal在内部都依赖ExecutionContext将内部数据存储在字典中。后者似乎为异步调用添加了另一层间接寻址CallCont
AsyncLocal
类,用于沿着异步控制流流动环境数据。我以前使用过CallContext.LogicalGet/SetData
来实现这一目的,我想知道这两者在语义上是否以及在哪些方面是不同的(除了明显的API差异,比如强类型和不依赖字符串键)
我想知道这两个词在语义上是否不同,以及在哪些方面不同
可以看出,CallContext
和asynchlocal
在内部都依赖ExecutionContext
将内部数据存储在字典中。后者似乎为异步调用添加了另一层间接寻址CallContext
自.NET远程处理以来就一直存在,它是一种在异步调用之间流动数据的便捷方式,而在此之前没有真正的替代方法
我能发现的最大区别是,asynchlocal
现在允许您在底层存储值发生更改时通过回调注册通知,无论是通过ExecutionContext
开关还是通过替换现有值
// AsyncLocal<T> also provides optional notifications
// when the value associated with the current thread
// changes, either because it was explicitly changed
// by setting the Value property, or implicitly changed
// when the thread encountered an "await" or other context transition.
// For example, we might want our
// current culture to be communicated to the OS as well:
static AsyncLocal<Culture> s_currentCulture = new AsyncLocal<Culture>(
args =>
{
NativeMethods.SetThreadCulture(args.CurrentValue.LCID);
});
//AsyncLocal还提供可选通知
//当与当前线程关联的值
//更改,或者是因为它已显式更改
//通过设置Value属性,或隐式更改
//当线程遇到“等待”或其他上下文转换时。
//例如,我们可能希望
//要传达给操作系统的当前文化:
静态AsyncLocal s_currentCulture=新的AsyncLocal(
args=>
{
NativeMethods.SetThreadCulture(args.CurrentValue.LCID);
});
除此之外,一个驻留在System.Threading
中,而另一个驻留在System.Runtime.Remoting
中,前者将在CoreCLR中得到支持
另外,似乎AsyncLocal
没有写时浅拷贝语义SetLogicalData
有,因此数据在调用之间流动而不被复制。语义基本相同。两者都存储在ExecutionContext
中,并通过异步调用流动
区别在于API更改(正如您所描述的)以及为值更改注册回调的能力
从技术上讲,实现上有很大的不同,因为每次复制CallContext
时都会克隆CallContext(使用CallContext.Clone
),而asynchLocal
的数据保存在ExecutionContext.\u localValues
字典中,只复制该引用而无需任何额外工作
为确保在更改AsyncLocal
的值时更新仅影响当前流,将创建一个新字典,并将所有现有值复制到新字典中
这种差异对性能既有好处,也有坏处,具体取决于asynchlocal
的使用位置
现在,正如Hans Passant在评论CallContext
中提到的,最初是为远程处理而设计的,在不支持远程处理的地方(例如Net Core)不可用,这可能就是为什么框架中添加了AsyncLocal
:
#if FEATURE_REMOTING
public LogicalCallContext.Reader LogicalCallContext
{
[SecurityCritical]
get { return new LogicalCallContext.Reader(IsNull ? null : m_ec.LogicalCallContext); }
}
public IllogicalCallContext.Reader IllogicalCallContext
{
[SecurityCritical]
get { return new IllogicalCallContext.Reader(IsNull ? null : m_ec.IllogicalCallContext); }
}
#endif
注意:VisualStudioSDK中还有一个AsyncLocal
,它基本上是CallContext
的包装器,它显示了这些概念的相似性:。在计时方面似乎存在一些语义差异
使用CallContext,当设置子线程/task/async方法的上下文时,即调用task.Factory.StartNew()、task.Run()或async方法时,会发生上下文更改
使用AsyncLocal,当子线程/任务/异步方法实际开始执行时,会发生上下文更改(调用更改通知回调)
时间上的差异可能会很有趣,特别是如果您希望在切换上下文时克隆上下文对象。使用不同的机制可能导致克隆不同的内容:使用CallContext,在创建子线程/任务或调用异步方法时克隆内容;但是使用AsyncLocal,当子线程/task/async方法开始执行时,克隆内容,上下文对象的内容可能已被父线程更改。我认为没有任何更改。对于不能依赖CallContext的项目,它是一种替代方案,因为它们以CoreCLR为目标。CallContext需要远程处理支持,在小型CLR版本中不可用。您是否可以发布一个快速的代码片段来演示这种差异?历史上(在.Net 4.6之前),我们必须在CallContext上添加一个特殊的插槽,以便在切换调用上下文时克隆上下文数据结构。演示程序显示,通过CallContext,克隆发生在调用线程上,而AsyncLocal则在被调用线程上调用更改通知处理程序。