最近怪事又開始發生了,IIS的應用程序池無做掛掉,都指向同一個矛頭,async,threadPool,Task,還有一個System.NullReferenceException,所以這些都讓我們感覺,我們的異步程序出現了問題,事實也是如此,我們的異步調用引用了對“上下文”的非空引用,最後導致w3wp進程死掉!
通過其它前輩的分享,找到了問題產生的原因,大叔也總結一下
1 async方法需要使用await等待它的結果,這樣可以保證你的SynchronizationContext上下文不為空,即不會出現非空引用的錯誤。
2 在調用async方法時,如果不方法加await關鍵字,也可以使用它的ConfigureAwait(false)方法,它雖然不會保存SynchronizationContext上下文,但它也不會報非空引用的錯誤。
3 在一個新線程裡調用async的異步方法,需要我們注意上面兩點
參看文章
http://www.cnblogs.com/cmt/p/configure_await_false.html
http://www.cnblogs.com/cmt/p/sokcet_memory_leak.html
技術點說明
1 Task.Run(()=>{}); 將一個任務添加到線程池裡,排隊執行
2 async 標識一個方法為異步方法,可以與主線程並行執行,發揮CPU的多核優勢
3 await 在調用一個async方法前可以添加這個修飾符,它意思是等待當前異步方法執行完後,再執行下面的代碼
4 ConfigureAwait(true),代碼由同步執行進入異步執行時,當前線程上下文信息就會被捕獲並保存至 SynchronizationContext中,供異步執行中使用,並且供異步執行完成之後的同步執行中使用
5 Configurewait(flase),不進行線程上下文信息的捕獲,async方法中與await之後的代碼執行時就無法獲取await之前的線程的上下文信息,在ASP.NET中最直接的影響就是HttpConext.Current的值為null,但不會出現非空引用的錯誤
Async引起的死鎖,w3wp.exe掛的原因
對於將異步方法偷懶的人,即使用Wait()和Result的人,將會為些付出代價,因為它會引起線程的死鎖,最終導致w3wp掛掉,注意在控制器console程序中,這件事不會發生。
當一個未完成的Task 被await的時候,當前的上下文將在該Task完成的時候重新獲得並繼續執行剩余的代碼。這個context就是當前的SynchronizationContext ,除非它是空的。GUI和ASP.NET 應用程序的SynchronizationContext 有排他性,只允許一個線程運行。當await 完成的時候,它試圖在它原來的代碼上下文執行它剩余的部分,但是該代碼上下文中已經有一個線程在了,就是那個一直在同步等待async 完成的那個線程,它們兩個相互等待,因此就死鎖了。
代碼如下
public class tools { public static async Task TestAsync() { await Task.Delay(1000); } } public class HomeController : Controller { public ActionResult Index() { tools.TestAsync().Wait();//產生死鎖,w3wp.exe掛掉 ViewBag.Message = "test"; return View(); } }
以上就是我們在解決由異步引起的w3wp.exe崩潰中所學習到的知識!
感謝各位的閱讀!