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

返回目錄html

最近怪事又開始發生了,IIS的應用程序池無作掛掉,都指向同一個矛頭,async,threadPool,Task,還有一個System.NullReferenceException,因此這些都讓咱們感受,咱們的異步程序出現了問題,事實也是如此,咱們的異步調用引用了對「上下文」的非空引用,最後致使w3wp進程死掉!
經過其它前輩的分享,找到了問題產生的緣由,大叔也總結一下
1 async方法須要使用await等待它的結果,這樣能夠保證你的SynchronizationContext上下文不爲空,即不會出現非空引用的錯誤。程序員

2 在調用async方法時,若是不方法加await關鍵字,也可使用它的ConfigureAwait(false)方法,它雖然不會保存SynchronizationContext上下文,但它也不會報非空引用的錯誤。編程

3 在一個新線程裏調用async的異步方法,須要咱們注意上面兩點app

參看文章異步

http://www.cnblogs.com/cmt/p/configure_await_false.htmlasync

http://www.cnblogs.com/cmt/p/sokcet_memory_leak.html異步編程

技術點說明學習

1 Task.Run(()=>{}); 將一個任務添加到線程池裏,排隊執行測試

2 async 標識一個方法爲異步方法,能夠與主線程並行執行,發揮CPU的多核優點ui

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程序中,這件事不會發生。

MSDN:https://msdn.microsoft.com/zh-cn/magazine/jj991977.aspx

 

始終使用 Async

 

異步代碼讓我想起了一個故事,有我的提出世界是懸浮在太空中的,可是一個老婦人當即提出質疑,她聲稱世界位於一個巨大烏龜的背上。 當這我的問烏龜站在哪裏時,老夫人回答:「很聰明,年輕人,下面是一連串的烏龜!」在將同步代碼轉換爲異步代碼時,您會發現,若是異步代碼調用其餘異步代碼而且被其餘異步代碼所調用,則效果最好 — 一路向下(或者也能夠說「向上」)。 其餘人已注意到異步編程的傳播行爲,並將其稱爲「傳染」或將其與殭屍病毒進行比較。 不管是烏龜仍是殭屍,不容置疑的是,異步代碼趨向於推進周圍的代碼也成爲異步代碼。 此行爲是全部類型的異步編程中所固有的,而不只僅是新 async/await 關鍵字。

 

「始終異步」表示,在未慎重考慮後果的狀況下,不該混合使用同步和異步代碼。 具體而言,經過調用 Task.Wait 或 Task.Result 在異步代碼上進行阻塞一般很糟糕。 對於在異步編程方面「淺嘗輒止」的程序員,這是個特別常見的問題,他們僅僅轉換一小部分應用程序,並採用同步 API 包裝它,以便代碼更改與應用程序的其他部分隔離。 不幸的是,他們會遇到與死鎖有關的問題。 在 MSDN 論壇、Stack Overflow 和電子郵件中回答了許多與異步相關的問題以後,我能夠說,迄今爲止,這是異步初學者在瞭解基礎知識以後最常提問的問題: 「爲什麼個人部分異步代碼死鎖?」

其中一個方法發生阻塞,等待 async 方法的結果。 此代碼僅在控制檯應用程序中工做良好,可是在從 GUI 或 ASP.NET 上下文調用時會死鎖。 此行爲可能會使人困惑,尤爲是經過調試程序單步執行時,這意味着沒完沒了的等待。 在調用 Task.Wait 時,致使死鎖的實際緣由在調用堆棧中上移。

這種死鎖的根本緣由是 await 處理上下文的方式。 默認狀況下,當等待未完成的 Task 時,會捕獲當前「上下文」,在 Task 完成時使用該上下文恢復方法的執行。 此「上下文」是當前 SynchronizationContext(除非它是 null,這種狀況下則爲當前 TaskScheduler)。 GUI 和 ASP.NET 應用程序具備 SynchronizationContext,它每次僅容許一個代碼區塊運行。 當 await 完成時,它會嘗試在捕獲的上下文中執行 async 方法的剩餘部分。 可是該上下文已含有一個線程,該線程在(同步)等待 async 方法完成。 它們相互等待對方,從而致使死鎖。

請注意,控制檯應用程序不會造成這種死鎖它們具備線程池 SynchronizationContext 而不是每次執行一個區塊的 SynchronizationContext,所以當 await 完成時,它會在線程池線程上安排 async 方法的剩餘部分。 該方法可以完成,並完成其返回任務,所以不存在死鎖。 當程序員編寫測試控制檯程序,觀察到部分異步代碼按預期方式工做,而後將相同代碼移動到 GUI 或 ASP.NET 應用程序中會發生死鎖,此行爲差別可能會使人困惑。

此問題的最佳解決方案是容許異步代碼經過基本代碼天然擴展。 若是採用此解決方案,則會看到異步代碼擴展到其入口點(一般是事件處理程序或控制器操做)。 控制檯應用程序不能徹底採用此解決方案,由於 Main 方法不能是 async。 若是 Main 方法是 async,則可能會在完成以前返回,從而致使程序結束。  控制檯應用程序的 Main 方法是代碼能夠在異步方法上阻塞爲數很少的幾種狀況之一。

 

代碼以下

 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();
        }
}

 在Task.Delay(1000)後面添加Configurewait(flase)能夠有效的避免代碼的死鎖!( 此時,當等待完成時,它會嘗試在線程池上下文中執行 async 方法的剩餘部分。 該方法可以完成,並完成其返回任務,所以不存在死鎖。 若是須要逐漸將應用程序從同步轉換爲異步,則此方法會特別有用。

以上就是咱們在解決由異步引發的w3wp.exe崩潰中所學習到的知識!

感謝各位的閱讀!

 返回目錄

相關文章
相關標籤/搜索