C# ExecutionContext 实现

网上关于ExecutionContext的说明比较少,我们来看看微软的描述吧,

 名称说明
System_CAPS_pubmethodSystem_CAPS_static Capture()

捕获从当前线程的执行上下文。

System_CAPS_pubmethod CreateCopy()

创建当前执行上下文的副本。

System_CAPS_pubmethod Dispose()

释放 ExecutionContext 类的当前实例所使用的所有资源。

System_CAPS_pubmethod Equals(Object)

确定指定的对象是否等于当前对象。(继承自 Object。)

System_CAPS_pubmethod GetHashCode()

作为默认哈希函数。(继承自 Object。)

System_CAPS_pubmethod GetObjectData(SerializationInfo, StreamingContext)

设置指定 SerializationInfo 重新创建当前执行上下文的实例所需的逻辑上下文信息的对象。

System_CAPS_pubmethod GetType()

获取当前实例的 Type。(继承自 Object。)

System_CAPS_pubmethodSystem_CAPS_static IsFlowSuppressed()

指示是否当前正在取消执行上下文的流动。

System_CAPS_pubmethodSystem_CAPS_static RestoreFlow()

在异步线程间恢复执行上下文的流动。

System_CAPS_pubmethodSystem_CAPS_static Run(ExecutionContext, ContextCallback, Object)

在当前线程上指定的执行上下文中运行的方法。

System_CAPS_pubmethodSystem_CAPS_static SuppressFlow()

在异步线程间取消执行上下文的流动。

System_CAPS_pubmethod 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 实例】

 

posted on 2017-11-26 11:59  dz45693  阅读(3441)  评论(0编辑  收藏  举报

导航