网上关于ExecutionContext的说明比较少,我们来看看微软的描述吧,
名称 | 说明 | |
---|---|---|
Capture() |
捕获从当前线程的执行上下文。 |
|
CreateCopy() |
创建当前执行上下文的副本。 |
|
Dispose() |
释放 ExecutionContext 类的当前实例所使用的所有资源。 |
|
Equals(Object) |
确定指定的对象是否等于当前对象。(继承自 Object。) |
|
GetHashCode() |
作为默认哈希函数。(继承自 Object。) |
|
GetObjectData(SerializationInfo, StreamingContext) |
设置指定 SerializationInfo 重新创建当前执行上下文的实例所需的逻辑上下文信息的对象。 |
|
GetType() | ||
IsFlowSuppressed() |
指示是否当前正在取消执行上下文的流动。 |
|
RestoreFlow() |
在异步线程间恢复执行上下文的流动。 |
|
Run(ExecutionContext, ContextCallback, Object) |
在当前线程上指定的执行上下文中运行的方法。 |
|
SuppressFlow() |
在异步线程间取消执行上下文的流动。 |
|
ToString() |
返回表示当前对象的字符串。(继承自 Object。) |
而实际开发中我们用的比较多的应该是SuppressFlow,RestoreFlow,Capture,CreateCopy和Run方法,比如我们在一个Barrier源码中,调用回调方法就是采用ExecutionContext 的Run方法,但是该方法需要一个ExecutionContext 实例,于是我们需要先捕获一个ExecutionContext 实例,然后拷贝再传递给Run方法,而有些时候我们又不想同步上下文,可以用SuppressFlow来暂停同步。这里需要借助一个AsyncFlowControl结构:
public struct AsyncFlowControl: IDisposable { private bool useEC; private ExecutionContext _ec; #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK private SecurityContext _sc; #endif // #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK private Thread _thread; #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK [SecurityCritical] internal void Setup(SecurityContextDisableFlow flags) { useEC = false; Thread currentThread = Thread.CurrentThread; _sc = currentThread.GetMutableExecutionContext().SecurityContext; _sc._disableFlow = flags; _thread = currentThread; } #endif // #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK [SecurityCritical] internal void Setup() { useEC = true; Thread currentThread = Thread.CurrentThread; _ec = currentThread.GetMutableExecutionContext(); _ec.isFlowSuppressed = true; _thread = currentThread; } public void Dispose() { Undo(); } [SecuritySafeCritical] public void Undo() { if (_thread == null) { throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CannotUseAFCMultiple")); } if (_thread != Thread.CurrentThread) { throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CannotUseAFCOtherThread")); } if (useEC) { if (Thread.CurrentThread.GetMutableExecutionContext() != _ec) { throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_AsyncFlowCtrlCtxMismatch")); } ExecutionContext.RestoreFlow(); } #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK else { if (!Thread.CurrentThread.GetExecutionContextReader().SecurityContext.IsSame(_sc)) { throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_AsyncFlowCtrlCtxMismatch")); } SecurityContext.RestoreFlow(); } #endif // #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK _thread = null; } }
AsyncFlowControl的code比较简单,里面有ExecutionContext和SecurityContext两个上下文,SecurityContext是一个很重要的 与认证权限有关的,这里我们忽略它,我们的核心主要关注ExecutionContext的实现方式和思路,注意Setup方法中【_ec = currentThread.GetMutableExecutionContext();_ec.isFlowSuppressed = true;】,现在我们再来看看ExecutionContext的实现:
public sealed class ExecutionContext : IDisposable, ISerializable { #if FEATURE_CAS_POLICY private HostExecutionContext _hostExecutionContext; #endif // FEATURE_CAS_POLICY private SynchronizationContext _syncContext; private SynchronizationContext _syncContextNoFlow; #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK private SecurityContext _securityContext; #endif // #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK #if FEATURE_REMOTING private LogicalCallContext _logicalCallContext; private IllogicalCallContext _illogicalCallContext; // this call context follows the physical thread #endif // #if FEATURE_REMOTING private Flags _flags; private Dictionary<IAsyncLocal, object> _localValues; private List<IAsyncLocal> _localChangeNotifications; public static AsyncFlowControl SuppressFlow() { if (IsFlowSuppressed()) { throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CannotSupressFlowMultipleTimes")); } Contract.EndContractBlock(); AsyncFlowControl afc = new AsyncFlowControl(); afc.Setup(); return afc; } public static void RestoreFlow() { ExecutionContext ec = Thread.CurrentThread.GetMutableExecutionContext(); if (!ec.isFlowSuppressed) { throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CannotRestoreUnsupressedFlow")); } ec.isFlowSuppressed = false; } public static bool IsFlowSuppressed() { return Thread.CurrentThread.GetExecutionContextReader().IsFlowSuppressed; } public static ExecutionContext Capture() { // set up a stack mark for finding the caller StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; return ExecutionContext.Capture(ref stackMark, CaptureOptions.None); } static internal ExecutionContext Capture(ref StackCrawlMark stackMark, CaptureOptions options) { ExecutionContext.Reader ecCurrent = Thread.CurrentThread.GetExecutionContextReader(); // check to see if Flow is suppressed if (ecCurrent.IsFlowSuppressed) return null; #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK // capture the security context SecurityContext secCtxNew = SecurityContext.Capture(ecCurrent, ref stackMark); #endif // #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK #if FEATURE_CAS_POLICY // capture the host execution context HostExecutionContext hostCtxNew = HostExecutionContextManager.CaptureHostExecutionContext(); #endif // FEATURE_CAS_POLICY SynchronizationContext syncCtxNew = null; #if FEATURE_REMOTING LogicalCallContext logCtxNew = null; #endif if (!ecCurrent.IsNull) { // capture the sync context if (0 == (options & CaptureOptions.IgnoreSyncCtx)) syncCtxNew = (ecCurrent.SynchronizationContext == null) ? null : ecCurrent.SynchronizationContext.CreateCopy(); #if FEATURE_REMOTING // copy over the Logical Call Context if (ecCurrent.LogicalCallContext.HasInfo) logCtxNew = ecCurrent.LogicalCallContext.Clone(); #endif // #if FEATURE_REMOTING } Dictionary<IAsyncLocal, object> localValues = null; List<IAsyncLocal> localChangeNotifications = null; if (!ecCurrent.IsNull) { localValues = ecCurrent.DangerousGetRawExecutionContext()._localValues; localChangeNotifications = ecCurrent.DangerousGetRawExecutionContext()._localChangeNotifications; } // // If we didn't get anything but defaults, and we're allowed to return the // dummy default EC, don't bother allocating a new context. // if (0 != (options & CaptureOptions.OptimizeDefaultCase) && #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK secCtxNew == null && #endif #if FEATURE_CAS_POLICY hostCtxNew == null && #endif // FEATURE_CAS_POLICY syncCtxNew == null && #if FEATURE_REMOTING (logCtxNew == null || !logCtxNew.HasInfo) && #endif // #if FEATURE_REMOTING localValues == null && localChangeNotifications == null ) { return s_dummyDefaultEC; } // // Allocate the new context, and fill it in. // ExecutionContext ecNew = new ExecutionContext(); #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK ecNew.SecurityContext = secCtxNew; if (ecNew.SecurityContext != null) ecNew.SecurityContext.ExecutionContext = ecNew; #endif #if FEATURE_CAS_POLICY ecNew._hostExecutionContext = hostCtxNew; #endif // FEATURE_CAS_POLICY ecNew._syncContext = syncCtxNew; #if FEATURE_REMOTING ecNew.LogicalCallContext = logCtxNew; #endif // #if FEATURE_REMOTING ecNew._localValues = localValues; ecNew._localChangeNotifications = localChangeNotifications; ecNew.isNewCapture = true; return ecNew; } public static void Run(ExecutionContext executionContext, ContextCallback callback, Object state) { if (executionContext == null) throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_NullContext")); if (!executionContext.isNewCapture) throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_NotNewCaptureContext")); Run(executionContext, callback, state, false); } internal static void Run(ExecutionContext executionContext, ContextCallback callback, Object state, bool preserveSyncCtx) { RunInternal(executionContext, callback, state, preserveSyncCtx); } internal static void RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, bool preserveSyncCtx) { Contract.Assert(executionContext != null); if (executionContext.IsPreAllocatedDefault) { Contract.Assert(executionContext.IsDefaultFTContext(preserveSyncCtx)); } else { Contract.Assert(executionContext.isNewCapture); executionContext.isNewCapture = false; } Thread currentThread = Thread.CurrentThread; ExecutionContextSwitcher ecsw = default(ExecutionContextSwitcher); RuntimeHelpers.PrepareConstrainedRegions(); try { ExecutionContext.Reader ec = currentThread.GetExecutionContextReader(); if ( (ec.IsNull || ec.IsDefaultFTContext(preserveSyncCtx)) && #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK SecurityContext.CurrentlyInDefaultFTSecurityContext(ec) && #endif // #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK executionContext.IsDefaultFTContext(preserveSyncCtx) && ec.HasSameLocalValues(executionContext) ) { // Neither context is interesting, so we don't need to set the context. // We do need to reset any changes made by the user's callback, // so here we establish a "copy-on-write scope". Any changes will // result in a copy of the context being made, preserving the original // context. EstablishCopyOnWriteScope(currentThread, true, ref ecsw); } else { if (executionContext.IsPreAllocatedDefault) executionContext = new ExecutionContext(); ecsw = SetExecutionContext(executionContext, preserveSyncCtx); } // // Call the user's callback // callback(state); } finally { ecsw.Undo(); } } public ExecutionContext CreateCopy() { if (!isNewCapture) { throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CannotCopyUsedContext")); } ExecutionContext ec = new ExecutionContext(); ec.isNewCapture = true; ec._syncContext = _syncContext == null ? null : _syncContext.CreateCopy(); ec._localValues = _localValues; ec._localChangeNotifications = _localChangeNotifications; #if FEATURE_CAS_POLICY // capture the host execution context ec._hostExecutionContext = _hostExecutionContext == null ? null : _hostExecutionContext.CreateCopy(); #endif // FEATURE_CAS_POLICY #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK if (_securityContext != null) { ec._securityContext = _securityContext.CreateCopy(); ec._securityContext.ExecutionContext = ec; } #endif // #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK #if FEATURE_REMOTING if (this._logicalCallContext != null) ec.LogicalCallContext = (LogicalCallContext)this.LogicalCallContext.Clone(); Contract.Assert(this._illogicalCallContext == null); #endif // #if FEATURE_REMOTING return ec; } internal static ExecutionContextSwitcher SetExecutionContext(ExecutionContext executionContext, bool preserveSyncCtx) { #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; #endif // #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK Contract.Assert(executionContext != null); Contract.Assert(executionContext != s_dummyDefaultEC); // Set up the switcher object to return; ExecutionContextSwitcher ecsw = new ExecutionContextSwitcher(); Thread currentThread = Thread.CurrentThread; ExecutionContext.Reader outerEC = currentThread.GetExecutionContextReader(); ecsw.thread = currentThread; ecsw.outerEC = outerEC; ecsw.outerECBelongsToScope = currentThread.ExecutionContextBelongsToCurrentScope; if (preserveSyncCtx) executionContext.SynchronizationContext = outerEC.SynchronizationContext; executionContext.SynchronizationContextNoFlow = outerEC.SynchronizationContextNoFlow; currentThread.SetExecutionContext(executionContext, belongsToCurrentScope: true); RuntimeHelpers.PrepareConstrainedRegions(); try { OnAsyncLocalContextChanged(outerEC.DangerousGetRawExecutionContext(), executionContext); #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK //set the security context SecurityContext sc = executionContext.SecurityContext; if (sc != null) { // non-null SC: needs to be set SecurityContext.Reader prevSeC = outerEC.SecurityContext; ecsw.scsw = SecurityContext.SetSecurityContext(sc, prevSeC, false, ref stackMark); } else if (!SecurityContext.CurrentlyInDefaultFTSecurityContext(ecsw.outerEC)) { // null incoming SC, but we're currently not in FT: use static FTSC to set SecurityContext.Reader prevSeC = outerEC.SecurityContext; ecsw.scsw = SecurityContext.SetSecurityContext(SecurityContext.FullTrustSecurityContext, prevSeC, false, ref stackMark); } #endif // #if FEATURE_IMPERSONATION || FEATURE_COMPRESSEDSTACK #if FEATURE_CAS_POLICY // set the Host Context HostExecutionContext hostContext = executionContext.HostExecutionContext; if (hostContext != null) { ecsw.hecsw = HostExecutionContextManager.SetHostExecutionContextInternal(hostContext); } #endif // FEATURE_CAS_POLICY } catch { ecsw.UndoNoThrow(); throw; } return ecsw; } }
从ExecutionContext的成员变量来看,ExecutionContext包含很多上下文的,HostExecutionContext,SynchronizationContext,SecurityContext,LogicalCallContext和IllogicalCallContext。
SuppressFlow实例化一个AsyncFlowControl然后调用SetUP方法【_ec = currentThread.GetMutableExecutionContext();_ec.isFlowSuppressed = true;】,RestoreFlow获取执行上下文【 ExecutionContext ec = Thread.CurrentThread.GetMutableExecutionContext()】,这2个方法的执行上细文是相同的。
接下来我们来看看Capture方法,首先获取ExecutionContext.Reader【线程上下文的一个包装】
internal ExecutionContext.Reader GetExecutionContextReader()
{
return new ExecutionContext.Reader(m_ExecutionContext);
}
然后检查IsFlowSuppressed,最后依次捕获上下文
1. SecurityContext secCtxNew = SecurityContext.Capture(ecCurrent, ref stackMark)
2. HostExecutionContext hostCtxNew = HostExecutionContextManager.CaptureHostExecutionContext()
3. syncCtxNew = (ecCurrent.SynchronizationContext == null) ? null : ecCurrent.SynchronizationContext.CreateCopy()
4. logCtxNew = ecCurrent.LogicalCallContext.Clone()
5. localValues = ecCurrent.DangerousGetRawExecutionContext()._localValues;localChangeNotifications = ecCurrent.DangerousGetRawExecutionContext()._localChangeNotifications;
这个我们捕获这些上下文,那么后面的CreateCopy其实也需要拷贝这些上下文的。
这里的Run方法,使用上比较好理解【调用ContextCallback传入特定线程的上下文】,但是代码层面就不是那么好理解了,里面还借助了ExecutionContextSwitcher对象,但是和兴实现是ecsw = SetExecutionContext(executionContext, preserveSyncCtx)【还原线程上下文】
如:
ExecutionContext ec = new ExecutionContext();
ec.isNewCapture = true;
ec._syncContext = _syncContext == null ? null : _syncContext.CreateCopy();
ec._localValues = _localValues;
ec._localChangeNotifications = _localChangeNotifications;
ec._hostExecutionContext = _hostExecutionContext == null ? null : _hostExecutionContext.CreateCopy();
if (_securityContext != null)
{
ec._securityContext = _securityContext.CreateCopy();
ec._securityContext.ExecutionContext = ec;
}
if (this._logicalCallContext != null)
ec.LogicalCallContext = (LogicalCallContext)this.LogicalCallContext.Clone();
所以从使用ExecutionContext 的角度来讲,还是很好理解的,先用Capture方法捕获线程上这些上下文保存到ExecutionContext 实例里面,最后在调用Run方法时需要还原线程的这些上下文【来源先前保存到ExecutionContext 实例】。