The semantics are pretty much the same. Both are stored in the ExecutionContext
and flow through async calls.
The differences are API changes (just as you described) together with the ability to register a callback for value changes.
Technically, there's a big difference in the implementation as the CallContext
is cloned each time it is copied (using CallContext.Clone
) while the AsyncLocal
's data is kept in the ExecutionContext._localValues
dictionary and just that reference is copied over without any extra work.
To make sure updates only affect the current flow when you change the AsyncLocal
's value a new dictionary is created and all the existing values are shallow-copied to the new one.
That difference can be both good and bad for performance, depending on where the AsyncLocal
is used.
Now, as Hans Passant mentioned in the comments CallContext
was originally made for remoting, and isn't available where remoting isn't supported (e.g. .Net Core) which is probably why AsyncLocal
was added to the framework:
#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
Note: there's also an AsyncLocal
in the Visual Studio SDK that is basically a wrapper over CallContext
which shows how similar the concepts are: Microsoft.VisualStudio.Threading.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…