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

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

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

      .net core 非阻塞的異步編程 及 線程調度過程

      本文主要分為三個部分:

      1、語法格式

      2、線程調度情況

      3、編程注意事項

      4、練一練

      * 閱讀提示 :鼠標懸停在 章節標題 上可見 文章目錄

       

       

      異步編程(Task Asynchronous Programming,TAP),一種編程模式(Task-based Asynchronous Pattern)。

      TAP 是 .NET 中推薦的異步編程模式,基于 Task 和 Task<TResult> 類型,用于表示異步。

      異步編程一般應對兩種場景,一是 I/O 綁定,當需要網絡連接(連接數據庫或讀寫到文件系統等)等耗時長的任務;二是 CPU 綁定,需要耗時長的計算。

       

       

      1、簡單的語法格式

      .net 一直在為開發人員簡化和標準化異步的寫法,從 .net 4.5 開始就已經支持使用 aysncawait 的關鍵字。

      異步的語法格式如下:

       

      private async Task<TResult> DoSomeStuffAsync(..)

      {

           ..

          await ..

          ..

      }

       

      l  關鍵詞 async 本身不具備什么意義,只是裝飾,當方法冠以 async 關鍵詞,方法體內允許使用 await

      await 是標記需要等待的地方,但其本質并非阻塞線程。

      l  “非阻止操作”:指當運行到 await 時,會把當前線程返回到上一級調用者繼續執行,如果沒有上一級調用者,則該線程當場釋放。

       

      非阻止操作

      阻止操作

      備注

      獲取任務返回 await task task.Wait / task.Result

      非阻塞:線程遇到 await 時會返回上一層調用者繼續執行,如果沒有上一級調用者,則釋放該線程;

      阻塞:線程在等待期間不能執行其他任務,也不釋放線程,硬等

       

      任一任務完成

      await Task.WhenAny

      Task.WaitAny

      所有任務完成

      await Task.WhenAll

      Task.WaitAll

      等待一段時間

      await Task.Delay

      Thread.Sleep

       

      l  異步方法返回的值總是 Task 的實例,可以是 Task 類型或 Task<TResult>,其中 TResult 是執行的方法的返回類型

      l  如果直接拿異步方法的結果,形如 var obj = GetSomethingAsync(),這個 obj 是一個 Task 對象,其中 obj.AsyncStatus,obj.Result 可見執行情況

      await + 執行異步方法,形如 await GetSomethingAsync() 會得到 TResult 實例

      l  直接使用 task.Result 得到的任務結果其狀態是未知的,應該使用 await task,保證任務是 Completed

      l  一般地,異步方法的名字需要添加后綴 Async,以便于寫代碼的時候區分開同步方法和異步方法

      l  等待異步任務的執行過程中,如果其中發生了錯誤,該異步任務的外層 try catch 會捕捉到,它也是一種任務結果

       

       

       

      2、異步運行機制,線程調度

      觀察以下代碼,思考一下控制臺會輸出什么?

      public static async Task Main(string[] args)
      {
          logMessage("Main <<----");
          var task = GetUrlContentLengthAsync();
          logMessage("Main ---->>");
          await task;
          logMessage("ALL COMPLETED");
      }
      
      static async Task<int> GetUrlContentLengthAsync()
      {
          logMessage("GetUrlContentLengthAsync start ");
      
          using var client = new HttpClient();
          Task<string> getStringTask = client.GetStringAsync("https://learn.microsoft.com/dotnet");
      
          // Do some independent work..
      
          var contents = await getStringTask;
      
          logMessage("GetUrlContentLengthAsync end ");
          return contents.Length;
      }
      
      static void logMessage(string msg)
      {
          Console.WriteLine($"{DateTime.Now.ToString("MM-dd HH:mm:ss.fff")} [{Thread.CurrentThread.ManagedThreadId}] {msg}");
      }

       

       

      打印結果:

      11-18 18:34:34.563 [1] Main <<----
      11-18 18:34:34.632 [1] GetUrlContentLengthAsync start
      11-18 18:34:34.810 [1] Main ---->>
      11-18 18:34:37.283 [7] GetUrlContentLengthAsync end
      11-18 18:34:37.286 [7] ALL COMPLETED

       

       

      先分析一下 GetUrlContentLengthAsync 這個異步方法,簡單歸納會存在以下步驟:

      1. httpClient.GetStringAsync 發起 HTTP 請求,并立即返回一個未完成的任務。
      2. await 關鍵字會暫停 GetStringAsync 方法的執行,并將控制權返回給調用方。
      3. 任務調度器通過操作系統的通知機制來監聽 HTTP 請求的響應。
      4. 操作系統在后臺監控 I/O 操作的狀態,并在操作完成時通知應用程序。
      5. 任務調度器隨后選擇一個可用的線程來繼續執行 異步方法的剩余部分(await 之后)。

       

       

      線程運行過程如下:

       

      * HTTP 請求是一個 I/O 操作,操作系統會通過 I/O 完成端口(IOCP)來處理這些操作。

      * IOCP 是一種高效的機制,用于處理異步 I/O 操作。它允許操作系統在 I/O 操作完成時通知應用程序。

       

       

       

      3、注意事項

      如果調用了 異步方法,一定要 await 任務執行結果

      反面示例:

      public static async Task Main(string[] args)
      {
          logMessage("Main <<----");
      
          // fault example: if do not wait the result then we will do not know what happened on it
          GetSomeStuff();
      
          logMessage("Main ---->>");
      } 

      這個異步任務已經在執行,但是卻沒有后續處理,不知道是成功或是失敗,又或者一直在執行沒有辦法停止,這是危險的。

       

       

      避免使用 task.Result,應該使用 await 

      直接使用 task.Result 的方式拿結果是線程阻塞的方式,也即如果任務還沒完成,那么這個線程將什么都不干,只等待任務完成。
      使用 await,而避免使用 xxTask.Result
      阻塞線程:如果任務尚未完成,訪問 Result 會阻塞當前線程,直到任務完成。這會導致性能問題,特別是在 UI 線程中使用時,會導致界面卡頓。
      死鎖風險:在某些情況下,特別是在同步上下文(如 UI 線程)中,訪問 Result 可能會導致死鎖(任務等待當前線程釋放,而當前線程又在等待任務完成)。
      異常處理:直接訪問 Result 可能會忽略任務中的異常。使用 await 可以更好地處理異常。

       

       

      是否線程安全

      異步編程的機制,允許到正在處理一個請求時,同時存在多個線程在處理操作,如果在對同一個對象做寫入操作,這是危險的。

      所以并行時的任務最好是沒有關系的。

      如果使用鎖,需要考慮是否會導致死鎖的問題。

       

       

      異步編程可能會增加代碼復雜度,慎重取舍

      異步編程并不一定帶來性能提升,畢竟上下文切換也是有開銷的,對于簡單的任務可能一條線做完的方式更合適。

       

       

       

       

       

      4、練一練

      以下代碼有什么問題?

      public async Task<ResultResponse> ValidateReceiptAsync(List<Guid> customerIds, ReceiptRequest request, CancellationToken cancellationToken)
      {
          var tokenTask = _medicalCheckServiceHelper.GetServiceToken(cancellationToken);
          
          var medicalProvidersTask = _medicalProviderRepo.GetMedicalProvidersNames();
      
          var customersTask = _customerService.GetByIdsAsync(customerIds);
          
          var checkResults = await Task.WhenAll(request.UuidList.Select(uuid => _medicalCheckServiceHelper.GetResultAsync(uuid, tokenTask.Result, cancellationToken)));
      
          return await ConsolidateReceiptsResult(medicalProvidersTask.Result, checkResults, await customersTask);
      } 

       

       

       

       

       

       

       

       

       

       

       

       

       

      思考一下

       

       

       

       

       

       

       

       

       

       

       

       

       

       

       

      • 1. tokenTask.Result 在任務完成之前訪問會導致阻塞,應該在 await tokenTask 之后再訪問。

       

      • 2. 確認哪些可以并行處理,注意線程安全

      這里有 4 個任務,分別是 獲取 token(假作 taskA)、獲取治療廠商(taskB)、獲取用戶信息(taskC)、獲取檢查結果(taskD)
      其中,taskD 依賴于 taskA ;現在需要確定 taskA、taskB、taskC 之間的依賴關系。

       

        • a. 假設真實場景如下:
          • i. taskA 取自于配置中心,也即它會走 HTTP 請求
          • ii. taskD 是一個第三方接口,也即一個 HTTP 請求
          • iii. taskB 和 taskC 取自同一個數據庫,并且它們共用了一個數據庫連接的上下文

      那么可能的修改是這樣的:

      public async Task<ResultResponse> ValidateReceiptAsync(List<string> customerIds, ReceiptRequest request, CancellationToken cancellationToken)
      {
          var tokenTask = _medicalCheckServiceHelper.GetServiceToken(cancellationToken);
          var medicalProvidersTask = _medicalProviderRepo.GetMedicalProvidersNames();
      
          var token = await tokenTask;
          var checkResults = await Task.WhenAll(request.UuidList.Select(uuid => _medicalCheckServiceHelper.GetResultAsync(uuid, token, cancellationToken)));
          
          var medicalProviders = await medicalProvidersTask;
          var customers = await _customerService.GetByIdsAsync(customerIds);
      
          return await ConsolidateReceiptsResult(medicalProviders, checkResults, customers);
      }

       

        • b. 假設真實場景如下:
          • i. taskA 取自于配置中心,也即它會走 HTTP 請求
          • ii. taskD 是一個第三方接口,也即一個 HTTP 請求
          • iii. taskB 和 taskC 取自不同數據庫或不同的 HTTP 請求,它們相互獨立

      那么可能的修改是這樣的:

      public async Task<ResultResponse> ValidateReceiptAsync(List<string> customerIds, ReceiptRequest request, CancellationToken cancellationToken)
      {
          var tokenTask = _medicalCheckServiceHelper.GetServiceToken(cancellationToken);
          var medicalProvidersTask = _medicalProviderRepo.GetMedicalProvidersNames();
          var customersTask = _customerService.GetByIdsAsync(customerIds);
      
          var token = await tokenTask;
          var checkResultsTask = Task.WhenAll(request.UuidList.Select(uuid => _medicalCheckServiceHelper.GetResultAsync(uuid, token, cancellationToken)));
      
          var medicalProviders = await medicalProvidersTask;
          var customers = await customersTask;
          var checkResults = await checkResultsTask;
          return await ConsolidateReceiptsResult(medicalProviders, checkResults, customers);
      }

       

      但其實并不盡完善,因為這種寫法的可讀性并沒有那么好,看起來更追求資源優化。
      所以說引入了 異步編程 的話,代碼是會變復雜的,每寫一步都需要慎重考慮。

       

       

       

       

       

       

      Reference:

      [1] The Task Asynchronous Programming (TAP) model with async and await" - C# | Microsoft Learn

      [2] Asynchronous programming scenarios - C# | Microsoft Learn

      [3] Asynchronous programming - C# | Microsoft Learn

       

      posted @ 2024-11-19 16:56  Carcar019  閱讀(303)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 91老熟女老女人国产老| 亚洲综合成人av在线| 日韩亚洲国产激情一区二区| 91人妻熟妇在线视频| 极品无码国模国产在线观看| 亚洲中文字幕国产精品| 亚洲狠狠婷婷综合久久久| 亚洲精品区午夜亚洲精品区| 影音先锋2020色资源网| 精品亚洲精品日韩精品| 久热这里只精品视频99| 亚洲AV无码国产永久播放蜜芽| 国产日韩入口一区二区| 日韩欧激情一区二区三区| 亚洲国产精品久久久天堂麻豆宅男| 国产午夜福利不卡在线观看| 国产99视频精品免费专区| 精品久久久久中文字幕日本| 国语精品国内自产视频| 九九在线精品国产| 夜夜添狠狠添高潮出水| 亚洲欧洲国产综合一区二区| 欧美性猛交xxxx免费看| 伊人激情一区二区三区av| 成人午夜大片免费看爽爽爽| 国产亚洲精品第一综合另类| 超清无码一区二区三区| 亚洲精品天堂在线观看| 国产AV大陆精品一区二区三区| 日本黄韩国色三级三级三| 免费看国产曰批40分钟| 狠狠色噜噜狠狠狠888米奇视频| 免费看成人毛片无码视频| 人妻蜜臀久久av不卡| 国产在线一区二区在线视频| 在线精品自拍亚洲第一区| 国内外成人综合免费视频| 小嫩批日出水无码视频免费| 中文字幕亚洲人妻一区| 国产精品妇女一区二区三区| 粗壮挺进人妻水蜜桃成熟|