程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#~異步編程再續~await與async引起的w3wp.exe崩潰-問題友好的解決,

C#~異步編程再續~await與async引起的w3wp.exe崩潰-問題友好的解決,

編輯:C#入門知識

C#~異步編程再續~await與async引起的w3wp.exe崩潰-問題友好的解決,


關於死鎖的原因

理解該死鎖的原因在於理解await 處理contexts的方式,默認的,當一個未完成的Task 被await的時候,當前的上下文將在該Task完成的時候重新獲得並繼續執行剩余的代碼。這個context就是當前的SynchronizationContext ,除非它是空的。WEB應用程序的SynchronizationContext 有排他性,只允許一個線程運行。當await 完成的時候,它試圖在它原來的代碼上下文執行它剩余的部分,但是該代碼上下文中已經有一個線程在了,就是那個一直在同步等待async 完成的那個線程,它們兩個相互等待,因此就死鎖了。

MSDN使用異步應該注意

規則
描述
例外
避免使用 async void
優先使用 async Task 而不用 async void
Event handlers
Async到頂
不要混合使用 blocking 和 async 的代碼
Console main method
注意配置好執行的context
盡量設置 ConfigureAwait(false)
需要context的除外

 

感恩的心

多謝網上很多文章的分享,在相關文章中找到了在同步代碼中使用異步代碼的無阻塞方案,之前也自己寫了幾個測試的DEMO,但Task<T>這種帶有返回值的異步方法還是會出現死鎖,之前代碼如下:

    /// <summary>
    /// 大叔測試
    /// </summary>
    public class tools
    {
        #region 假設這些方法被第三方被封裝的,不可修改的方法
        public static async Task TestAsync()
        {
            await Task.Delay(1000)
                      .ConfigureAwait(false);//不會死鎖
        }

        public static async Task<string> GetStrAsync()
        {
            return await Task.Run(() => "OK").ConfigureAwait(false);
        }

        public static async Task DelayTestAsync()
        {
            Logger.LoggerFactory.Instance.Logger_Info("DelayAsync");
            await Task.Delay(1000);
        }

        public static async Task<string> DelayGetStrAsync()
        {
            return await Task.Run(() => "OK");
        }
        #endregion

        #region 我們需要在自己代碼中封裝它,解決線程死鎖
        /// <summary>
        /// 沒有返回值的同步調用異步的實體
        /// </summary>
        /// <param name="func"></param>
        public static void ForWait(Func<Task> func)
        {
            func().ConfigureAwait(false);
        }
        /// <summary>
        /// 存在返回值的同步調用異步的實體
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="func"></param>
        /// <returns></returns>
        public static T ForResult<T>(Func<Task<T>> func)
        {
            var a = func();
            a.ConfigureAwait(false);
            return a.Result;
        }
        #endregion
    }

對於上面的代碼,當執行一個Task反回類型(即無返回結果)時,程序是沒有問題的,但可以存在返回結果,那麼上面的ForResult方法依舊會產生死鎖!執著的我當然不會就此罷休,找了一些文章後,終於還是有了結果,在對當前上下文和異步上下文進行了簡

單的處理後,最終還是實現了同步與異步的並存所以說,人是最聰明的一種動物,一切都皆有可能,只要你想!

Lind.DDD.Utils.AsyncTaskManager代碼如下,希望可以給大家帶來一些啟發和幫助

 

  /// <summary>
    /// 異步線程管理-在同步程序中調用異步,解決了線程死鎖問題
    /// </summary>
    public class AsyncTaskManager
    {
        /// <summary>
        /// 運行無返回類型的異步方法
        /// </summary>
        /// <param name="task"></param>
        public static void RunSync(Func<Task> task)
        {
            var oldContext = SynchronizationContext.Current;//同步上下文 
            var synch = new ExclusiveSynchronizationContext();//異步上下文
            SynchronizationContext.SetSynchronizationContext(synch);//設置當前同步上下文
            synch.Post(async obj =>
            {
                try
                {
                    await task();
                }
                catch (Exception e)
                {
                    synch.InnerException = e;
                    throw;
                }
                finally
                {
                    synch.EndMessageLoop();
                }
            }, null);
            synch.BeginMessageLoop();
            SynchronizationContext.SetSynchronizationContext(oldContext);
        }
        /// <summary>
        /// 運行返回類型為T的異步方法
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="task"></param>
        /// <returns></returns>
        public static T RunSync<T>(Func<Task<T>> task)
        {
            var oldContext = SynchronizationContext.Current;
            var synch = new ExclusiveSynchronizationContext();
            SynchronizationContext.SetSynchronizationContext(synch);
            T ret = default(T);//動作的默認值
            synch.Post(async obj =>
            {
                try
                {
                    ret = await task();
                }
                catch (Exception e)
                {
                    synch.InnerException = e;
                    throw;
                }
                finally
                {
                    synch.EndMessageLoop();
                }
            }, null);
            synch.BeginMessageLoop();
            SynchronizationContext.SetSynchronizationContext(oldContext);
            return ret;
        }

        /// <summary>
        /// 異步上下文對象
        /// </summary>
        class ExclusiveSynchronizationContext : SynchronizationContext
        {
            private bool done;
            public Exception InnerException { get; set; }
            readonly AutoResetEvent workItemsWaiting = new AutoResetEvent(false);
            readonly Queue<Tuple<SendOrPostCallback, object>> items =
             new Queue<Tuple<SendOrPostCallback, object>>();

            public override void Send(SendOrPostCallback d, object state)
            {
                throw new NotSupportedException("We cannot send to our same thread");
            }
            /// <summary>
            /// 添加到異步隊列
            /// </summary>
            /// <param name="d"></param>
            /// <param name="state"></param>
            public override void Post(SendOrPostCallback d, object state)
            {
                lock (items)
                {
                    items.Enqueue(Tuple.Create(d, state));
                }
                workItemsWaiting.Set();
            }
            /// <summary>
            /// 異步結束
            /// </summary>
            public void EndMessageLoop()
            {
                Post(obj => done = true, null);
            }
            /// <summary>
            /// 處理異步隊列中的消息
            /// </summary>
            public void BeginMessageLoop()
            {
                while (!done)
                {
                    Tuple<SendOrPostCallback, object> task = null;
                    lock (items)
                    {
                        if (items.Count > 0)
                        {
                            task = items.Dequeue();
                        }
                    }
                    if (task != null)
                    {
                        task.Item1(task.Item2);
                        if (InnerException != null) // the method threw an exeption
                        {
                            throw new AggregateException("AsyncInline.Run method threw an exception.",
                             InnerException);
                        }
                    }
                    else
                    {
                        workItemsWaiting.WaitOne();
                    }
                }
            }
            public override SynchronizationContext CreateCopy()
            {
                return this;
            }
        }
    }

最後我們進行測試中,看到線程沒有出現死鎖問題!

大叔相關await&async的文章列表

C#~異步編程再續~await與async引起的w3wp.exe崩潰

C#~異步編程再續~async異步方法與同步方法的並行

基礎才是重中之重~多線程的代價~我的內存都被吃了!

EF架構~EF異步改造之路~倉儲接口的改造~續

EF架構~EF異步改造之路~讓DbContextRepository去實現異步接口

EF架構~EF異步改造之路~倉儲接口的改造

C#~異步編程續~.net4.5主推的await&async應用

C#~異步編程

感謝各位的閱讀!

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved