C# ExecutionContext 實現

網上關於ExecutionContext的說明比較少,咱們來看看微軟的描述吧,異步

  名稱 說明
System_CAPS_pubmethodSystem_CAPS_static Capture()

捕獲從當前線程的執行上下文。async

System_CAPS_pubmethod CreateCopy()

建立當前執行上下文的副本。函數

System_CAPS_pubmethod Dispose()

釋放 ExecutionContext 類的當前實例所使用的全部資源。ui

System_CAPS_pubmethod Equals(Object)

肯定指定的對象是否等於當前對象。(繼承自 Object。)this

System_CAPS_pubmethod GetHashCode()

做爲默認哈希函數。(繼承自 Object。)spa

System_CAPS_pubmethod GetObjectData(SerializationInfo, StreamingContext)

設置指定 SerializationInfo 從新建立當前執行上下文的實例所需的邏輯上下文信息的對象。線程

System_CAPS_pubmethod GetType()

獲取當前實例的 Type。(繼承自 Object。)3d

System_CAPS_pubmethodSystem_CAPS_static IsFlowSuppressed()

指示是否當前正在取消執行上下文的流動。rest

System_CAPS_pubmethodSystem_CAPS_static RestoreFlow()

在異步線程間恢復執行上下文的流動。code

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 實例】

相關文章
相關標籤/搜索