<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      [譯]async/await中阻塞死鎖

          這篇博文主要是講解在async/await中使用阻塞式代碼導致死鎖的問題,以及如何避免出現這種死鎖。內容主要是從作者Stephen Cleary的兩篇博文中翻譯過來.

      原文1Don'tBlock on Async Code

      原文2why the AspNetSynchronizationContext was removed

       

      示例代碼:async_await中阻塞死鎖.rar

       

      一、async/await 異步代碼運行流程

               async/await是在.NET4.5版本引入的關鍵字,讓開發者可以更加輕松的創建異步方法。

               我們從下圖來認識async/await的運行流程:

       

      二、在異步代碼中阻塞,導致死鎖的示例

      UI 示例

               單擊一個按鈕,將發起一個REST遠程請求并且將結果顯示到textbox控件上。(這是一個Windows Forms程序,同樣也適用于其他任何UI應用程序)

      // My "library" method.
      public static async Task<JObject> GetJsonAsync(Uri uri)
      {
        using (var client = new HttpClient())
        {
          var jsonString = await client.GetStringAsync(uri);
          return JObject.Parse(jsonString);
        }
      }
      
      
      // My "top-level" method.
      public void Button1_Click(...)
      {
        var jsonTask = GetJsonAsync(...);
        textBox1.Text = jsonTask.Result;
      }

           類庫方法GetJsonAsync發起REST遠程請求并且將結果解析為JSON返回。Button1_Click方法調用Task .Result阻塞等待GetJsonAsync處理完畢并顯示結果

               這段代碼會死鎖。

      ASP.NET 示例

               在類庫方法GetJsonAsync中發起一個REST遠程請求,這次這個GetJsonAsyncASP.NET context中被調用。(示例是Web API項目,同樣適用于任何一個ASP.NET應用程序 - 注:非ASP.NET Core應用)

      // My "library" method.
      public static async Task<JObject> GetJsonAsync(Uri uri)
      {
        using (var client = new HttpClient())
        {
          var jsonString = await client.GetStringAsync(uri);
          return JObject.Parse(jsonString);
        }
      }
      
       
      // My "top-level" method.
      public class MyController : ApiController
      {
        public string Get()
        {
          var jsonTask = GetJsonAsync(...);
          return jsonTask.Result.ToString();
        }
      }

           這段代碼也會死鎖。與UI示例是同一個原因。

       

      園友在《傳統asp.net小心 async/await坑》這篇文章中抓到一個異常信息,我自己沒注意抓異常,死鎖后就關閉調試了。不關閉調試,死鎖一段時間應該會報下面錯誤。

      在 System.Web.ThreadContext.AssociateWithCurrentThread(Boolean setImpersonationContext)
      在 System.Web.HttpApplication.OnThreadEnterPrivate(Boolean setImpersonationContext)
      在 System.Web.LegacyAspNetSynchronizationContext.CallCallbackPossiblyUnderLock(SendOrPostCallback callback, Object state)
      在 System.Web.LegacyAspNetSynchronizationContext.CallCallback(SendOrPostCallback callback, Object state)
      在 System.Web.LegacyAspNetSynchronizationContext.Post(SendOrPostCallback callback, Object state)
      在 System.Threading.Tasks.SynchronizationContextAwaitTaskContinuation.PostAction(Object state)
      在 System.Threading.Tasks.AwaitTaskContinuation.RunCallback(ContextCallback callback, Object state, Task& currentTask)
      --- 引發異常的上一位置中堆棧跟蹤的末尾 ---
      在 System.Threading.Tasks.AwaitTaskContinuation.<>c.<ThrowAsyncIfNecessary>b__18_0(Object s)
      在 System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state)
      在 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
      在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
      在 System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
      在 System.Threading.ThreadPoolWorkQueue.Dispatch()
      在 System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

       

      三、是什么原因導致的死鎖呢?

               await一個Task后,在恢復繼續執行時,會試圖進入await之前的context

               第一個示例中,這個contextUI context(任何UI應用,除了控制臺應用)。在第二個示例中,這個contextASP.NET request context

               另一個需要注意的點:ASP.NET request context 沒有綁定到特定的線程上(像UI context一樣),但是request context同一時刻只允許被綁定到一個線程上。我曾經在MSDN上有發表過關于SynchronzationContext的文章.

               死鎖是怎么發生的呢?我們從top-level方法開始(UIButton1_Click方法或ASP.NETMyContoller.Get方法)

      1.         top-level方法調用GetJsonAsync(在UI/ASP.NET context中)。

      2.         GetJsonAsync通過HttpClient.GetStringAsync發起REST遠程請求(在UI/ASP.NET context中)。

      3.         GetStringAsync返回一個未完成Task,標識REST遠程請求還未處理完

      4.         GetJsonAsync方法中await GetStringAsync返回的未完成Task。等Task執行完畢,

      會重新捕獲等待之前的context并使用它繼續執行GetJsonAsync

      5.         GetJsonAsyncawait后,攜帶context的線程會跳出GetJsonAsync方法,繼續執行后面的代碼。并在jsonTask.Result發生阻塞。此時攜帶context的線程被阻塞了。

      6.         最終,REST請求處理完,GetStringAsync返回的Task處理完成。

      7.         GetJsonAsync方法準備繼續運行,并且等待context可用,以便在context中執行。

      8.         發生context死鎖。在top-level方法中已經阻塞了攜帶context的線程,等待GetJsonAsync返回的Task完成。而此時,GetJsonAsync方法正在等待context被釋放,以便在context中繼續執行。

       

      四、防止死鎖

               這里有三個最佳實踐來避免這種死鎖(更詳細傳送門)。

      1.         在你的”library”異步方法中,返回未完成Task時都調用ConfigureAwait(false)

      2.         始終使用 Async不要混合阻塞式代碼和異步代碼

      3.         ASP.NET 升級為ASP.NET Core。在ASP.NET Core框架中,已經移除SynchronizationContext

      按照第一條最佳實踐,”library”中的異步方法修改如下:

      public static async Task<JObject> GetJsonAsync(Uri uri)
      {
        using (var client = new HttpClient())
        {
          var jsonString = await client.GetStringAsync(uri).ConfigureAwait(false);
          return JObject.Parse(jsonString);
        }
      }

      ConfigureAwait(continueOnCapturedContext: false)continueOnCapturedContext參數表示是否嘗試將延續任務封送回原始上下文。

      ConfigureAwait(false)改變了GetJsonAsync的延續行為,使它不用在原來的context中恢復。GetJsonAsync將直接在線程池線程中恢復,這使得GetJsonAsync能完成任務,并且無需重新進入原來的context

       

               按照第二條最佳實踐。修改”top-level”方法如下:

      public async void Button1_Click(...)
      {
        var json = await GetJsonAsync(...);
        textBox1.Text = json;
      }
      
      
      public class MyController : ApiController
      {
        public async Task<string> Get()
        {
          var json = await GetJsonAsync(...);
          return json.ToString();
        }
      }

              這樣修改,改變了top-level方法的阻塞行為。所有的等待都是異步等待,這樣context就不會被阻塞。

      其他異步等待指導原則:

      執行以下操作

      不要使用以下方式

      使用以下方式

      創建Task

      Task constructor

      Task.Run or TaskFactory.StartNew

      檢索后臺任務的結果

      Task.Wait Task.Result

      await

      等待任何任務完成

      Task.WaitAny

      await Task.WhenAny

      檢索多個任務的結果

      Task.WaitAll

      await Task.WhenAll

      等待一段時間

      Thread.Sleep

      await Task.Delay

       

      五、在ASP.NET Core框架中,已經移除SynchronizationContext

      為什么AspNetSynchronizationContextASP.NET Core中被移除。盡管我不知道ASP.NET團隊內部是什么觀點,但我認為的觀點是兩個:性能和簡單。

      性能方面:

      在沒有SynchronizationContextASP.NET Core中,當一個async/await異步處理恢復執行時,會從線程池中獲取一個線程并且執行繼續操作。

      1.         避免了把操作排隊到request context隊列(request context同一時刻只允許被綁定到一個線程上)

      2.         避免了因為攜帶 request context 的線程被阻塞而發生“死鎖”

      3.         不需要重新進入request context(重新進入request context涉及到很多內部作業任務,例如:設置HttpContext.Current和當前線程的身份標識(identity)和語言(culture))

      簡單化:

      舊版本ASP.NETSynchronizationContext工作的很好,但是也存在棘手的問題,特別是在身份管理方面(參考:Thread.CurrentPrincipal VS HttpContext.Current.User)

       

      六、async/await避免阻塞式死鎖最佳實踐

      1.    在你的“library”異步方法中,返回未完成Task時都調用ConfigureAwait(false),標識不需要將延續任務封送回原始上下文。

      盡管在ASP.NET Core中,不用再調用ConfigureAwait(false)來防止死鎖。但由于你提供的“類庫”可能被用于UI 應用(egwinformwpf等)、舊版本ASP.NET應用、其他還存在context的應用。

      2.    更好的解決方案是“始終使用 async,不要混合阻塞式代碼和異步代碼”。

      因為當你在異步代碼中阻塞程序,將失去異步代碼帶來的所有好處。并且異步代碼釋放當前線程帶來的伸縮性也會失效。

      3.    ASP.NET項目升級為ASP.NET Core項目

      4.    (譯者加入)使用ABP框架中使用Fody編譯時編織,讓其帶上ConfigureAwait(false) 。設置方法:在程序集中加入這些文件(Use Fody ConfigureAwait library instead of manual ConfigureAwait calls.),最后的效果如下圖。這樣子這個程序集中就不用手動調用ConfigureAwait(false) 了

             

       

      推薦閱讀:

         Async/Await FAQ原文##譯文

         Stephen Cleary 的開源組件:AsyncEx

              Async/Await 異步編程中的最佳做法(原文##譯文

              (xishuai)ASP.NET混合阻塞式代碼和異步代碼,線程切換變化

       

      posted on 2019-01-09 08:32  滴答的雨  閱讀(7938)  評論(13)    收藏  舉報

      主站蜘蛛池模板: 果冻传媒mv免费播放在线观看| 久久精品娱乐亚洲领先| 久久国产精品久久精品国产| 少妇高潮喷水久久久影院| 精品无码久久久久久尤物| 99久久亚洲综合精品成人网| 国产欧美日韩视频怡春院| 亚洲精品二区在线播放| 色综合久久夜色精品国产| 精品三级在线| 欧美国产日韩久久mv| 日本无产久久99精品久久| 妺妺窝人体色www看美女| 亚洲男人天堂2018| 久久人体视频| 亚洲成av人片色午夜乱码| 日韩国产中文字幕精品| 欧美乱大交aaaa片if| www国产成人免费观看视频| 亚洲女人天堂| 精品久久久中文字幕一区| 日日碰狠狠躁久久躁96avv| 九九热免费公开视频在线| 熟女少妇精品一区二区| 九九热精品免费视频| 亚洲AV永久天堂在线观看| 成A人片亚洲日本久久| 国产99视频精品免费专区| 中文字幕精品av一区二区五区| 亚洲综合伊人久久综合| 久久一本人碰碰人碰| 999福利激情视频| 亚洲精品日韩在线丰满| 亚洲岛国成人免费av| 香港三级韩国三级日本三级| 精品熟女少妇免费久久| 日韩高清亚洲日韩精品一区二区| 日本怡春院一区二区三区| 成人无码潮喷在线观看| 久久久精品人妻一区二区三区| 湾仔区|