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

  • 相关阅读:
    改主机风扇转速
    PyTorch Live get started from Windows
    完全离线环境下安装Docker,Dockercompose,Airflow
    业余无线电爱好者,自制天线比较容易上手天线“莫克森天线”Moxon
    冠状动脉疾病患者的永久监测(GeneXus物联网应用)
    GeneXus开发示例瑞索纳控股有限公司
    超级App如何工作?
    2022年低代码关键点:GeneXus即将推出什么!
    Elementdialog创建新建页面
    element 表单校验与移除表单检验
  • 原文地址:https://www.cnblogs.com/majiang/p/7898663.html
Copyright © 2020-2023  润新知