理解該死鎖的原因在於理解await 處理contexts的方式,默認的,當一個未完成的Task 被await的時候,當前的上下文將在該Task完成的時候重新獲得並繼續執行剩余的代碼。這個context就是當前的SynchronizationContext ,除非它是空的。WEB應用程序的SynchronizationContext 有排他性,只允許一個線程運行。當await 完成的時候,它試圖在它原來的代碼上下文執行它剩余的部分,但是該代碼上下文中已經有一個線程在了,就是那個一直在同步等待async 完成的那個線程,它們兩個相互等待,因此就死鎖了。
規則
描述
例外
避免使用 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; } } }
最後我們進行測試中,看到線程沒有出現死鎖問題!
C#~異步編程再續~await與async引起的w3wp.exe崩潰
C#~異步編程再續~async異步方法與同步方法的並行
基礎才是重中之重~多線程的代價~我的內存都被吃了!
EF架構~EF異步改造之路~倉儲接口的改造~續
EF架構~EF異步改造之路~讓DbContextRepository去實現異步接口
EF架構~EF異步改造之路~倉儲接口的改造
C#~異步編程續~.net4.5主推的await&async應用
C#~異步編程
感謝各位的閱讀!