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;}}}最后我們進行測試中,看到線程沒有出現死鎖問題!
感謝各位的閱讀!
返回目錄
轉載于:https://www.cnblogs.com/lori/p/5481820.html
總結
以上是生活随笔為你收集整理的C#~异步编程再续~await与async引起的w3wp.exe崩溃-问题友好的解决的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: centos6/7安装gitlab
- 下一篇: CSS相邻兄弟选择器