C# 異步編程(async和await)


 

1. 源起

在我們從 .Net 轉 .Net Core 的過程中,我們通常會查看一些源碼或者看書進行學習,在其中你可能會看到類似於這樣的代碼

 1 // 1. Asp.Net Core 的 控制器 類
 2 [HttpGet]
 3 public async Task<IActionResult> Index()
 4 {
 5      ......
 6      await ......
 7      ......
 8 }
 9 
10 // 2. Console 的 Program.cs 文件
11 static async Task Main(string[] args)
12 {
13      ......
14      await ......
15      ......
16 }

這是一個很重要的 “新特性”---異步編程。在 C# 里面異步編程是通過關鍵字 async 和 await 兩個關鍵字實現的。

那什么是異步編程?async、await是怎么樣實現異步編程的呢?

2. 為什么要異步編程

在介紹異步編程之前,我們先說一下同步編程。

通常我們編寫的代碼在被執行時,通常是自上而下一行一行的執行,如過是執行方法,則進入方法內部繼續一行一行執行。

這就是同步執行,也稱之為同步編程(也可以說是編程)。

你會發現同步編程有一個問題,就是但是當一個方法執行耗時比較長時(例如讀取文件內容),會阻塞下面的代碼執行,整個軟件都會進入等待狀態(如是GUI界面,則執行操作的線程會阻塞,整個操作界面也會處於等待狀態),體驗度相當的不好---尤其是在計算機有多核的情況下,完全可以在另一個CPU上干其他的工作,同時計算機完成耗時任務的時候通知你。這就是異步編程的起源更多示例

3. C# 異步編程

通過使用異步編程,你可以避免性能瓶頸並增強應用程序的總體響應能力。 但是,編寫異步應用程序的傳統技術可能比較復雜,使它們難以編寫、調試和維護。

雖然 .Net 中有幾種異步編程模式,但是目前建議使用的只有一種(TAP),我們將對歷史有一些簡單的說明,對於EAP進行更深入的介紹。

3.1 .Net 提供了3中異步編程模型(更多介紹

  • TAP(基於任務的異步模式):使用單一方法表示異步操作的開始和結束,是.Net中進行異步編程的推薦方法。(.Net Framework 4中引入的)
  • EAP(基於事件的異步模式):提供異步行為的基於事件的舊模型。 這種模式需要后綴為 Async 的方法,以及一個或多個事件、事件處理程序委托類型和 EventArg 派生類型。 EAP 是在 .NET Framework 2.0 中引入的。 不建議新的開發使用此模式。
  • APM(異步編程模型):(也稱為 IAsyncResult 模式),這是使用 IAsyncResult 接口提供異步行為的舊模型。 在這種模式下,同步操作需要 Begin 和 End 方法(例如,BeginWrite 和 EndWrite以實現異步寫入操作)。 不建議新的開發使用此模式。

3.2 TAP(基於任務的異步模式)

C# 擁有語言級別的異步編程模型,它遵循基於任務的異步模式。

C# 異步編程的核心是 Task 和 Task<T> 對象(深入了解Task和Task<T>),這兩個對象對異步操作建模。它們受關鍵字 async 和 await 的支持(二者是異步編程的核心關鍵字)。通過這兩個關鍵字,可以使用 .NET Framework、.NET Core 或 Windows 運行時中的資源,輕松創建異步方法(幾乎與創建同步方法一樣輕松)。 使用 async 關鍵字定義的異步方法簡稱為“異步方法”。await 關鍵字控制執行 await 的方法的調用方,且它最終允許 UI 具有響應性或服務具有靈活性。

4. 異步編程 示例

 

  1 using System;
  2 using System.Threading;
  3 using System.Threading.Tasks;
  4 
  5 namespace ConsoleApp
  6 {
  7     class Program
  8     {
  9         static async Task Main(string[] args)
 10         {
 11             try
 12             {
 13                 //線程池,最大和最小數量(CompletionPortThreads 異步I/O線程數量)
 14                 //相關資料:https://docs.microsoft.com/zh-cn/dotnet/standard/threading/the-managed-thread-pool
 15                 ThreadPool.GetMinThreads(out int minWorkerThreads, out int minCompletionPortThreads);       //6 6
 16                 ThreadPool.GetMaxThreads(out int maxWorkerThreads, out int maxCompletionPortThreads);       //32767 1000
 17 
 18                 ShowMsg(minWorkerThreads.ToString());
 19                 ShowMsg(minCompletionPortThreads.ToString());
 20                 ShowMsg(maxWorkerThreads.ToString());
 21                 ShowMsg(maxCompletionPortThreads.ToString());
 22 
 23                 #region 【1. 使用示例】
 24                 ////測試async和await(運行在ThreadPool)
 25                 ////相關資料:https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/async/task-asynchronous-programming-model
 26                 //ShowMsg($"【Main start 1】");
 27                 //Test1Async();        //異步執行,但是不知道什么時候執行
 28                 //Test11Async();       //異步執行,但是不知道什么時候執行
 29                 //ShowMsg($"【Main end 1】");
 30                 //ShowMsg($"【Main start 2】");
 31                 //await Test2Async();  //同步執行,等待任務執行完成
 32                 //ShowMsg($"【Main end 2】");
 33 
 34                 //ShowMsg($"【Main start 3】");
 35                 //Task<String> task = Test3Async();    //有返回值的方法,異步執行,但是不知道什么時候執行
 36                 //ShowMsg($"【Main end Task】");
 37                 //ShowMsg($"{task.Result}");        //等待異步操作執行完成之后,獲取執行結果(相當於await)
 38 
 39                 //ShowMsg($"【Main end 3】");
 40 
 41                 //TaskRun();
 42 
 43                 //ShowMsg($"【Used samples end】");
 44 
 45                 #endregion
 46 
 47                 #region 【2. 錯誤捕獲示例】
 48                 ////相關資料:https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/try-catch#async-method-example
 49                 //// sample 1
 50                 //await Exception1Async();
 51 
 52                 //// sample 2
 53                 //Task<String> exceptionSample1 = Exception1Async();
 54                 //exceptionSample1.Wait();
 55 
 56                 //// sample 3
 57                 //Console.WriteLine(exceptionSample1.Result);
 58 
 59                 //// sample 4
 60                 //Exception1Async();
 61 
 62                 // 結論:
 63                 //     以上四種示例,前三種會進入外層try-catch,第四種則不會(因為是異步執行,不需要等待回傳結果,需要自行處理報錯信息)
 64 
 65                 //if (null != exceptionSample1.Exception)
 66                 //{
 67                 //}
 68                 #endregion
 69 
 70             }
 71             catch (Exception ex)
 72             {
 73                 Console.WriteLine(ex);
 74             }
 75 
 76             ShowMsg("The End!");
 77         }
 78 
 79         static async Task Test1Async()
 80         {
 81             ShowMsg($"Test start 1");
 82             await Task.Delay(300);
 83             ShowMsg($"Test end 1");
 84         }
 85 
 86         static async Task Test11Async()
 87         {
 88             ShowMsg($"Test start 1111");
 89             await Task.Delay(300);
 90             ShowMsg($"Test end 1111");
 91         }
 92 
 93         static async Task Test2Async()
 94         {
 95             ShowMsg($"Test start 2");
 96             await Task.Delay(300);
 97             ShowMsg($"Test end 2");
 98         }
 99 
100         static async Task<String> Test3Async()
101         {
102             ShowMsg($"Test start 3");
103             await Task.Delay(300);
104             ShowMsg($"Test end 3");
105             return "123";
106         }
107 
108         static void ShowMsg(String msg)
109         {
110             Console.WriteLine($" Time:{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fffff") } . Message: {msg}");
111         }
112 
113         static async Task<String> Exception1Async()
114         {
115             await Task.Delay(100);
116 
117             // Uncomment each of the following lines to
118             // demonstrate exception handling.
119 
120             throw new OperationCanceledException("canceled");
121             throw new Exception("Something happened.");
122             return "Done";
123         }
124 
125         static async Task TaskRun()
126         {
127             await Task.Run(() =>
128             {
129                 Thread.Sleep(100);
130                 ShowMsg("This is task run method.");
131             });
132         }
133 
134     }
135 }

 

 

 

附錄

異步編程

https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Asynchronous/Concepts

 

異步編程模式

https://docs.microsoft.com/zh-cn/dotnet/standard/asynchronous-programming-patterns/


C# 異步編程
https://docs.microsoft.com/zh-cn/dotnet/csharp/async

使用 Async 和 Await 的異步編程
https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/async/

異步編程模型
https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/async/task-asynchronous-programming-model

try-catch(C# 參考)
https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/try-catch

托管線程池
https://docs.microsoft.com/zh-cn/dotnet/standard/threading/the-managed-thread-pool

深入了解異步
https://docs.microsoft.com/zh-cn/dotnet/standard/async-in-depth

基於任務的異步模式
https://docs.microsoft.com/zh-cn/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap

 

任務並行庫

https://docs.microsoft.com/zh-cn/dotnet/standard/parallel-programming/task-parallel-library-tpl


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM