async不得不说的事:SynchronizationContext

待写

ASP.NET环境:

    internal sealed class AspNetSynchronizationContext : AspNetSynchronizationContextBase {

        // we move all of the state to a separate field since our CreateCopy() method needs shallow copy semantics
        private readonly State _state;

        internal AspNetSynchronizationContext(ISyncContext syncContext)
            : this(new State(new SynchronizationHelper(syncContext))) {
        }

        private AspNetSynchronizationContext(State state) {
            _state = state;
        }

        internal override bool AllowAsyncDuringSyncStages {
            get {
                return _state.AllowAsyncDuringSyncStages;
            }
            set {
                _state.AllowAsyncDuringSyncStages = value;
            }
        }

        // We can't ever truly disable the AspNetSynchronizationContext, as the user and runtime can kick off asynchronous
        // operations whether we wanted them to or not. But this property can be used as a flag by Page and other types
        // to signal that asynchronous operations are not currently valid, so at least ASP.NET can avoid kicking them
        // off and can bubble an appropriate exception back to the developer.
        internal override bool Enabled {
            get { return _state.Enabled; }
        }

        internal override ExceptionDispatchInfo ExceptionDispatchInfo {
            get { return _state.Helper.Error; }
        }

        internal override int PendingOperationsCount {
            get { return _state.Helper.PendingCount; }
        }

        internal override void AllowVoidAsyncOperations() {
            _state.AllowVoidAsyncOperations = true;
        }

        [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", Justification = "Used only during debug.")]
        internal override void AssociateWithCurrentThread() {
            IDisposable disassociationAction = _state.Helper.EnterSynchronousControl();

#if DBG
            IDisposable capturedDisassociationAction = disassociationAction;
            Thread capturedThread = Thread.CurrentThread;
            disassociationAction = new DisposableAction(() => {
                Debug.Assert(capturedThread == Thread.CurrentThread, String.Format("AssociateWithCurrentThread was called on thread ID '{0}', but DisassociateFromCurrentThread was called on thread ID '{1}'.", capturedThread.ManagedThreadId, Thread.CurrentThread.ManagedThreadId));
                capturedDisassociationAction.Dispose();
            });
#endif

            // Don't need to synchronize access to SyncControlDisassociationActions since only one thread can call
            // EnterSynchronousControl() at a time.
            _state.SyncControlDisassociationActions.Push(disassociationAction);
        }

        internal override void ClearError() {
            _state.Helper.Error = null;
        }

        // Called by the BCL when it needs a SynchronizationContext that is identical to the existing context
        // but does not have referential equality.
        public override SynchronizationContext CreateCopy() {
            return new AspNetSynchronizationContext(_state);
        }

        internal override void Disable() {
            _state.Enabled = false;
        }

        internal override void DisassociateFromCurrentThread() {
            // Don't need to synchronize access to SyncControlDisassociationActions since we assume that our callers are
            // well-behaved and won't call DisassociateFromCurrentThread() on a thread other than the one which called
            // AssociateWithCurrentThread(), which itself serializes access.
            Debug.Assert(_state.SyncControlDisassociationActions.Count > 0, "DisassociateFromCurrentThread() was called on a thread which hadn't previously called AssociateWithCurrentThread().");
            IDisposable disassociationAction = _state.SyncControlDisassociationActions.Pop();
            disassociationAction.Dispose();
        }

        internal override void Enable() {
            _state.Enabled = true;
        }

        public override void OperationCompleted() {
            Interlocked.Decrement(ref _state.VoidAsyncOutstandingOperationCount); // this line goes first since ChangeOperationCount might invoke a callback which depends on this value
            _state.Helper.ChangeOperationCount(-1);
        }

        public override void OperationStarted() {
            // If the caller tries to kick off an asynchronous operation while we are not
            // processing an async module, handler, or Page, we should prohibit the operation.
            if (!AllowAsyncDuringSyncStages && !_state.AllowVoidAsyncOperations) {
                InvalidOperationException ex = new InvalidOperationException(SR.GetString(SR.Async_operation_cannot_be_started));
                throw ex;
            }

            _state.Helper.ChangeOperationCount(+1);
            Interlocked.Increment(ref _state.VoidAsyncOutstandingOperationCount);
        }

        // Dev11 Bug 70908: Race condition involving SynchronizationContext allows ASP.NET requests to be abandoned in the pipeline
        //
        // When the last completion occurs, the _pendingCount is decremented and then the _lastCompletionCallbackLock is acquired to get
        // the _lastCompletionCallback.  If the _lastCompletionCallback is non-null, then the last completion will invoke the callback;
        // otherwise, the caller of PendingCompletion will handle the completion.
        internal override bool PendingCompletion(WaitCallback callback) {
            return _state.Helper.TrySetCompletionContinuation(() => callback(null));
        }

        public override void Post(SendOrPostCallback callback, Object state) {
            _state.Helper.QueueAsynchronous(() => callback(state));
        }

        // The method is used to post async func.
        internal void PostAsync(Func<object, Task> callback, Object state) {
            _state.Helper.QueueAsynchronousAsync(callback, state);
        }

        internal override void ProhibitVoidAsyncOperations() {
            _state.AllowVoidAsyncOperations = false;

            // If the caller tries to prohibit async operations while there are still some
            // outstanding, we should treat this as an error condition. We can't throw from
            // this method since (a) the caller generally isn't prepared for it and (b) we
            // need to wait for the outstanding operations to finish anyway, so we instead
            // need to mark the helper as faulted.
            //
            // There is technically a race condition here: the caller isn't guaranteed to
            // observe the error if the operation counter hits zero at just the right time.
            // But it's actually not terrible if that happens, since the error is really
            // just meant to be used for diagnostic purposes.
            if (!AllowAsyncDuringSyncStages && Volatile.Read(ref _state.VoidAsyncOutstandingOperationCount) > 0) {
                InvalidOperationException ex = new InvalidOperationException(SR.GetString(SR.Async_operation_cannot_be_pending));
                _state.Helper.Error = ExceptionDispatchInfo.Capture(ex);
            }
        }

        internal override void ResetSyncCaller() {
            // no-op
            // this type doesn't special-case asynchronous work kicked off from a synchronous handler
        }

        internal override void SetSyncCaller() {
            // no-op
            // this type doesn't special-case asynchronous work kicked off from a synchronous handler
        }

        public override void Send(SendOrPostCallback callback, Object state) {
            _state.Helper.QueueSynchronous(() => callback(state));
        }

        private sealed class State {
            internal bool AllowAsyncDuringSyncStages = AppSettings.AllowAsyncDuringSyncStages;
            internal volatile bool AllowVoidAsyncOperations = false;
            internal bool Enabled = true;
            internal readonly SynchronizationHelper Helper; // handles scheduling of the asynchronous tasks
            internal Stack<IDisposable> SyncControlDisassociationActions = new Stack<IDisposable>(capacity: 1);
            internal int VoidAsyncOutstandingOperationCount = 0;

            internal State(SynchronizationHelper helper) {
                Helper = helper;
            }
        }

    }
    internal sealed class SynchronizationHelper {

        private Task _completionTask; // the Task that will run when all in-flight operations have completed
        private Thread _currentThread; // the Thread that's running the current Task; all threads must see the same value for this field
        private Task _lastScheduledTask = CreateInitialTask(); // the last Task that was queued to this helper, used to hook future Tasks (not volatile since always accessed under lock)
        private Task _lastScheduledTaskAsync = CreateInitialTask(); // the last async Task that was queued to this helper
        private readonly object _lockObj = new object(); // synchronizes access to _lastScheduledTask
        private int _operationsInFlight; // operation counter
        private readonly ISyncContext _syncContext; // a context that wraps an operation with pre- and post-execution phases
        private readonly Action<bool> _appVerifierCallback; // for making sure that developers don't try calling us after the request has completed

        public SynchronizationHelper(ISyncContext syncContext) {
            _syncContext = syncContext;
            _appVerifierCallback = AppVerifier.GetSyncContextCheckDelegate(syncContext);
        }

        // If an operation results in an exception, this property will provide access to it.
        public ExceptionDispatchInfo Error { get; set; }

        // Helper to access the _currentThread field in a thread-safe fashion.
        // It is not enough to mark the _currentThread field volatile, since that only guarantees
        // read / write ordering and doesn't ensure that each thread sees the same value.
        private Thread CurrentThread {
            get { return Interlocked.CompareExchange(ref _currentThread, null, null); }
            set { Interlocked.Exchange(ref _currentThread, value); }
        }

        // Returns the number of pending operations
        public int PendingCount { get { return ChangeOperationCount(0); } }

        public int ChangeOperationCount(int addend) {
            int newOperationCount = Interlocked.Add(ref _operationsInFlight, addend);
            if (newOperationCount == 0) {
                // if an asynchronous completion operation is queued, run it
                Task completionTask = Interlocked.Exchange(ref _completionTask, null);
                if (completionTask != null) {
                    completionTask.Start();
                }
            }

            return newOperationCount;
        }

        private void CheckForRequestStateIfRequired(bool checkForReEntry) {
            if (_appVerifierCallback != null) {
                _appVerifierCallback(checkForReEntry);
            }
        }

        // Creates the initial hook that future operations can ride off of
        private static Task CreateInitialTask() {
            return Task.FromResult<object>(null);
        }

        // Takes control of this SynchronizationHelper instance synchronously. Asynchronous operations
        // will be queued but will not be dispatched until control is released (by disposing of the
        // returned object). This operation might block if a different thread is currently in
        // control of the context.
        public IDisposable EnterSynchronousControl() {
            if (CurrentThread == Thread.CurrentThread) {
                // If the current thread already has control of this context, there's nothing extra to do.
                return DisposableAction.Empty;
            }

            // used to mark the end of the synchronous task
            TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
            Task lastTask;
            lock (_lockObj) {
                lastTask = _lastScheduledTask;
                _lastScheduledTask = tcs.Task; // future work can be scheduled off this Task
            }

            // The original task may end up Faulted, which would make its Wait() method throw an exception.
            // To avoid this, we instead wait on a continuation which is always guaranteed to complete successfully.
            if (!lastTask.IsCompleted) { lastTask.ContinueWith(_ => { }, TaskContinuationOptions.ExecuteSynchronously).Wait(); }
            CurrentThread = Thread.CurrentThread;

            // synchronous control is released by marking the Task as complete
            return new DisposableAction(() => {
                CurrentThread = null;
                tcs.TrySetResult(null);
            });
        }

        public void QueueAsynchronous(Action action) {
            CheckForRequestStateIfRequired(checkForReEntry: true);
            ChangeOperationCount(+1);

            // This method only schedules work; it doesn't itself do any work. The lock is held for a very
            // short period of time.
            lock (_lockObj) {
                Task newTask = _lastScheduledTask.ContinueWith(_ => SafeWrapCallback(action));
                _lastScheduledTask = newTask; // the newly-created task is now the last one
            }
        }

        // QueueAsynchronousAsync and SafeWrapCallbackAsync guarantee:
        // 1. For funcs posted here, it's would first come, first complete.
        // 2. There is no overlapping execution.
        public void QueueAsynchronousAsync(Func<object, Task> func, object state) {
            CheckForRequestStateIfRequired(checkForReEntry: true);
            ChangeOperationCount(+1);

            // This method only schedules work; it doesn't itself do any work. The lock is held for a very
            // short period of time.
            lock (_lockObj) {
                // 1. Note that we are chaining newTask with _lastScheduledTaskAsync, not _lastScheduledTask.
                // Chaining newTask with _lastScheduledTask would cause deadlock.
                // 2. Unwrap() is necessary to be called here. When chaining multiple tasks using the ContinueWith
                // method, your return type will be Task<T> whereas T is the return type of the delegate/method
                // passed to ContinueWith. As the return type of an async delegate is a Task, you will end up with
                // a Task<Task> and end up waiting for the async delegate to return you the Task which is done after
                // the first await.
                Task newTask = _lastScheduledTaskAsync.ContinueWith(
                    async _ => { await SafeWrapCallbackAsync(func, state); }).Unwrap();
                _lastScheduledTaskAsync = newTask; // the newly-created task is now the last one
            }
        }

        public void QueueSynchronous(Action action) {
            CheckForRequestStateIfRequired(checkForReEntry: false);
            if (CurrentThread == Thread.CurrentThread) {
                // current thread already owns the context, so just execute inline to prevent deadlocks
                action();
                return;
            }

            ChangeOperationCount(+1);
            using (EnterSynchronousControl()) {
                SafeWrapCallback(action);
            }
        }

        private void SafeWrapCallback(Action action) {
            // This method will try to catch exceptions so that they don't bubble up to our
            // callers. However, ThreadAbortExceptions will continue to bubble up.
            try {
                CurrentThread = Thread.CurrentThread;
                ISyncContextLock syncContextLock = null;
                try {
                    syncContextLock = (_syncContext != null) ? _syncContext.Enter() : null;
                    try {
                        action();
                    }
                    catch (Exception ex) {
                        Error = ExceptionDispatchInfo.Capture(ex);
                    }
                }
                finally {
                    if (syncContextLock != null) {
                        syncContextLock.Leave();
                    }
                }
            }
            finally {
                CurrentThread = null;
                ChangeOperationCount(-1);
            }
        }

        // This method does not run the func by itself. It simply queues the func into the existing
        // syncContext queue.
        private async Task SafeWrapCallbackAsync(Func<object, Task> func, object state) {
            try {
                TaskCompletionSource<Task> tcs = new TaskCompletionSource<Task>();
                QueueAsynchronous(() => {
                    var t = func(state);
                    t.ContinueWith((_) => {
                        if (t.IsFaulted) {
                            tcs.TrySetException(t.Exception.InnerExceptions);
                        }
                        else if (t.IsCanceled) {
                            tcs.TrySetCanceled();
                        }
                        else {
                            tcs.TrySetResult(t);
                        }
                    }, TaskContinuationOptions.ExecuteSynchronously);
                });
                await tcs.Task;
            }
            catch (Exception ex) {
                Error = ExceptionDispatchInfo.Capture(ex);
            }
            finally {
                ChangeOperationCount(-1);
            }
        }

        // Sets the continuation that will asynchronously execute when the pending operation counter
        // hits zero. Returns true if asynchronous execution is expected, false if the operation
        // counter is already at zero and the caller should run the continuation inline.
        public bool TrySetCompletionContinuation(Action continuation) {
            int newOperationCount = ChangeOperationCount(+1); // prevent the operation counter from hitting zero while we're setting the field
            bool scheduledAsynchronously = (newOperationCount > 1);
            if (scheduledAsynchronously) {
                Interlocked.Exchange(ref _completionTask, new Task(continuation));
            }

            ChangeOperationCount(-1);
            return scheduledAsynchronously;
        }

    }
 internal abstract class AspNetSynchronizationContextBase : SynchronizationContext {

        private AllowAsyncOperationsBlockDisposable _allowAsyncOperationsBlockDisposable;

        internal abstract bool AllowAsyncDuringSyncStages { get; set; }
        internal abstract bool Enabled { get; }
        internal Exception Error {
            get {
                ExceptionDispatchInfo dispatchInfo = ExceptionDispatchInfo;
                return (dispatchInfo != null) ? dispatchInfo.SourceException : null;
            }
        }
        internal abstract ExceptionDispatchInfo ExceptionDispatchInfo { get; }
        internal abstract int PendingOperationsCount { get; }

        internal abstract void ClearError();
        internal abstract void Disable();
        internal abstract void Enable();

        internal abstract bool PendingCompletion(WaitCallback callback);

        // A helper method which provides a Task-based wrapper around the PendingCompletion method.
        // NOTE: The caller should verify that there are never outstanding calls to PendingCompletion
        // or to WaitForPendingOperationsAsync, since each call replaces the continuation that will
        // be invoked.
        internal Task WaitForPendingOperationsAsync() {
            TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();

            WaitCallback callback = _ => {
                Exception ex = Error;
                if (ex != null) {
                    // We're going to observe the exception in the returned Task. We shouldn't keep
                    // it around in the SynchronizationContext or it will fault future Tasks.
                    ClearError();
                    tcs.TrySetException(ex);
                }
                else {
                    tcs.TrySetResult(null);
                }
            };

            if (!PendingCompletion(callback)) {
                // If PendingCompletion returns false, there are no pending operations and the
                // callback will not be invoked, so we should just signal the TCS immediately.
                callback(null);
            }

            return tcs.Task;
        }

        // These methods are used in the synchronous handler execution step so that a synchronous IHttpHandler
        // can call asynchronous methods without locking on the HttpApplication instance (possibly causing
        // deadlocks).

        internal abstract void SetSyncCaller();
        internal abstract void ResetSyncCaller();

        // These methods are used for synchronization, e.g. to create a lock that is tied to the current
        // thread. The legacy implementation locks on the HttpApplication instance, for example.

        internal abstract void AssociateWithCurrentThread();
        internal abstract void DisassociateFromCurrentThread();

        // These methods are used for telling the synchronization context when it is legal for an application
        // to kick off async void methods. They are used by the "AllowAsyncDuringSyncStages" setting to
        // determine whether kicking off an operation should throw.

        internal virtual void AllowVoidAsyncOperations() { /* no-op by default */ }
        internal virtual void ProhibitVoidAsyncOperations() { /* no-op by default */ }

        // helper method for wrapping AllowVoidAsyncOperations / ProhibitVoidAsyncOperations in a using block
        internal IDisposable AllowVoidAsyncOperationsBlock() {
            if (_allowAsyncOperationsBlockDisposable == null) {
                _allowAsyncOperationsBlockDisposable = new AllowAsyncOperationsBlockDisposable(this);
            }

            AllowVoidAsyncOperations();
            return _allowAsyncOperationsBlockDisposable;
        }

        // Helper method to wrap Associate / Disassociate calls in a using() statement
        internal IDisposable AcquireThreadLock() {
            AssociateWithCurrentThread();
            return new DisposableAction(DisassociateFromCurrentThread);
        }

        private sealed class AllowAsyncOperationsBlockDisposable : IDisposable {
            private readonly AspNetSynchronizationContextBase _syncContext;

            public AllowAsyncOperationsBlockDisposable(AspNetSynchronizationContextBase syncContext) {
                _syncContext = syncContext;
            }

            public void Dispose() {
                _syncContext.ProhibitVoidAsyncOperations();
            }
        }

    }
时间: 2024-10-01 09:17:46

async不得不说的事:SynchronizationContext的相关文章

Java中与String有关的且不得不说的事

如果以下有与Java堆栈和JVM常量池有关的可以访问下面的连接 Java当中的堆与栈详细解析 JVM中的常量池解析 前期记得自己学习编程语言的时候,只要能写出代码就可以.编程语言不就是写程序的吗?自己报着这种目的性很强的心态去学习,忽略了其中的精彩部分,学完C++之间学Java感觉Java中的String和C++中的没有什么区别,都是能存储大小随意变化的字符串. 如:String s="12345"; s  = "0";如果是字符串数组的话那就不,感觉很方便很好用.

极光推送那些不得不说的事

这个名字够骚的,没办法 问题尚未解决 同志们还需努力啊! 完整代码:http://download.csdn.net/detail/onebelowzero2012/9311039 废话不多说 上整理后的代码: package com.example.jpushdemo; import android.app.Application;import android.util.Log;import cn.jpush.android.api.JPushInterface; /** * For deve

网赚类APP与积分墙之间不得不说的事

5月8日,这一天App Store排行榜发生了一件诡异的事情,后来被移动业内称为“停摆”的事件,排行榜停摆,第一影响到的就是广告投放了,榜单不变的情况下.所有的投放都是白费.影响最大的当属5月8日的积分墙广告了,完全无效. 国内主流移动广告平台的业务主要集中积分墙业务这一块.当天跟排行榜相关的第三产业倒大霉,网赚类APP这个游离于行业边缘的圈子亦未能例外. l 网赚APP 手机赚钱行业始于2010年,各大智能手机开始普及市场,新平台创造了无限商机,改变人们日常,也包括赚钱方式.在短短几年间,手赚

Async/Await 异步编程中的最佳做法

近日来,涌现了许多关于 Microsoft .NET Framework 4.5 中新增了对 async 和 await 支持的信息. 本文旨在作为学习异步编程的“第二步”:我假设您已阅读过有关这一方面的至少一篇介绍性文章. 本文不提供任何新内容,Stack Overflow.MSDN 论坛和 async/await FAQ 这类在线资源提供了同样的建议. 本文只重点介绍一些淹没在文档海洋中的最佳做法. 本文中的最佳做法更大程度上是“指导原则”,而不是实际规则. 其中每个指导原则都有一些例外情况

异步编程中的最佳做法(Async/Await) --转

近日来,涌现了许多关于 Microsoft .NET Framework 4.5 中新增了对 async 和 await 支持的信息. 本文旨在作为学习异步编程的“第二步”:我假设您已阅读过有关这一方面的至少一篇介绍性文章. 本文不提供任何新内容,Stack Overflow.MSDN 论坛和 async/await FAQ 这类在线资源提供了同样的建议. 本文只重点介绍一些淹没在文档海洋中的最佳做法. 本文中的最佳做法更大程度上是“指导原则”,而不是实际规则. 其中每个指导原则都有一些例外情况

从零开始的JS生活(二)——BOM、DOM与JS中的事件

上回书说道,JS中变量.运算符.分支结构.循环和嵌套循环等内容.本回就由本K给大伙唠唠JS中的BOM.DOM和事件. 一."花心大萝卜"--BOM 1.震惊,FFF团为何对BOM举起了火把--BOM简介 BOM(Browser Object Model) 是指浏览器对象模型,在JS中BOM是个不折不扣的花心大萝卜,因为它有很多个对象,其中代表浏览器窗口的Window对象是BOM的"正室".也就是最重要的,其他对象都是正室的下手.或者叫侧室也不足为过. 2.细数BOM

七夕她生辰,岁月,请你不要伤害她,请你善待她

    岁月,请你不要伤害她   今天妈妈生日,我在外面很想念她,昨天晚上给她打了问候电话,她身体状况一般,但是精神状态还蛮好的,叫我不要担心,她在家里都挺好的. 记得今年6月末的一天,妈妈又一次因为全身抽筋严重住院了,那天我在公司上班,接到弟弟的电话,着急.忧愁.忏悔.无奈等各种情绪一起涌上心头.县里医院说送来的时候有轻微发烧迹象,经过退烧处理后,已经不抽筋,烧也退了.为了核查真实的抽筋原因,不过医生说需要先进的仪器做个全身检查(不能用农合卡报销),我弟弟同意进行了检查,大概查到脑部有3公分的

***防火墙密码恢复手记

***防火墙密码恢复手记 公司在用的一款***防火墙,密码意外遗失,无法登陆管理平台.虽然防火墙可以正常工作,但却无法修改配置,不能根增加和删除访问列表中的IP地址,不能调整访问策略.防火墙默认仅开通https web管理界面,未开启telnet.ssh等其他管理通道. 联系***厂家寻求技术支持,被告知必须返厂更换芯片,费用大约在2000元左右(网上搜了一下,几乎所有密码遗失的客户最终都只能选择返厂).公司用于该网络联网的仅此一台防火墙设备,终端数量在500以上,无其他硬件备份方案.因用户众多

外语学习的真实方法及误区

目录 前言 1. 对外语学习的主要误区 2. 造成“哑巴英语”的最主要原因 3. 英语环境的问题 4. 特殊群体 5. 语言关键期之争 6. 语言条件反射 7. 关于记忆力 8. 中国“英”雄 9. 二律背反? 10. 世纪之战 11.“学得”-“获得”之辩 12. 克氏理论 实践篇 第一阶段.早期(Early Stage) 1. 看图识音 2. 全身反应法(Total Physical Response) 第二阶段.提高期(Upping the Ante) 1. 听力内容设计 2. 纽约人在北