C# async 和 await 理解


C# async 和 await 理解

先假設如下場景:

主函數 Main,循環等待用戶輸入;

計算函數 Cal,耗時計算大量數據;

class Test { static int Main(string[] args) { while(true) { // 等待用戶輸入 } } public static int Cal() { int sum = 0; for (int i = 0; i < 999; i++) { sum = sum + i; } Console.WriteLine($"sum={sum}"); return sum; } }

為了在Main函數中調用Cal函數,同時Cal函數不阻塞主函數的循環,此時需要考慮增加一個CalAsync函數使Cal函數異步執行。

傳統的思維方法  在CalAsync函數中啟動一個線程,並在線程中執行Cal函數:

// using System.Threading; public static CalAsync() { Thread td = new Thread(new ThreadStart(Cal)); td.start(); }

這種方法顯示地創建了一個線程並啟動執行,CalAsync函數本身還是在主線程執行並且無法直接獲取Cal函數的結果。

async 和 await 異步編程方法  使用async標記CalAsync函數,並在CalAsync函數中創建任務Task異步執行Cal函數,同時使用await標記獲取Cal函數的執行結果:

// using System.Threading.Tasks; public static aysnc void CalAsync() { int result = await Task.Run(new Func<int>(Cal)); // 或使用lambda書寫方式 // int result = await Task.Run(() => test()); Console.WriteLine(result); }

在Main函數中直接調用CalAsync函數,可以發現CalAsync成功調用了Cal函數並在一段時間后輸出了結果,同時Main函數並不會被阻塞。

分別在Main、Cal、CalAsync函數中增加代碼打印當前線程ID:

class Test
{
    static void Main(string[] args) { string tid = Thread.CurrentThread.ManagedThreadId.ToString(); Console.WriteLine($"Main1 tid {tid}"); Task<int> t = CalAsync(); Console.WriteLine($"Main after CalAsync"); Console.Read(); } public static int Cal() { string tid = Thread.CurrentThread.ManagedThreadId.ToString(); Console.WriteLine($"Cal tid {tid}"); int sum = 0; for (int i = 0; i < 999; i++) { sum = sum + i; } Console.WriteLine($"sum={sum}"); return sum; } public static async Task<int> CalAsync() { string tid = Thread.CurrentThread.ManagedThreadId.ToString(); Console.WriteLine($"CalAsync1 tid {tid}"); int result = await Task.Run(new Func<int>(Cal)); tid = Thread.CurrentThread.ManagedThreadId.ToString(); Console.WriteLine($"CalAsync2 tid {tid}, result={result}"); return result; } }

結果如圖:  

C# async 和 await 理解

 

可以看出,在CalAsync函數中,await標記之前,代碼在主線程中執行,而await標記之后,代碼在子線程中執行。

理解與結論:

  • 在C#中, async標記了一個包含異步執行的函數,通過async標記的函數若在主線程中直接調用,則函數一開始仍在主線程中執行;

  • aysnc標記的函數內部必須包含await標記需要異步執行的函數(根據vs2017編譯提示),若當前函數在主線程中直接調用,則await標記前的代碼在主線程中執行,await標記后的代碼在其異步子線程中執行;

  • async標記的函數返回值必須為void、Task、Task< TResult> 類型,可以理解為async標記的函數返回的是 “空”、“即將執行的任務”、“帶結果的即將執行的任務”實例;

  • async標記的函數可以繼續往下調用async標記函數,調用形式如下例, 從調用邏輯可以理解為await實際上用來觸發所標記的Task任務異步執行,並最后獲取異步執行的返回值,從運行過程看該觸發應該僅對最終的Task任務有效:

 public static async Task<int> CallCalAsync() { string tid = Thread.CurrentThread.ManagedThreadId.ToString(); Console.WriteLine($"CallCalAsync1 tid {tid}"); int result = await CalAsync(); tid = Thread.CurrentThread.ManagedThreadId.ToString(); Console.WriteLine($"CallCalAsync2 tid {tid}, result={result}"); return result; }

 

 

總結

C#中async與await異步編程,可以理解為:

1. async聲明了一個包含異步執行代碼的函數,該函數執行時不會阻塞調用線程;

2. await存在於async函數中,聲明了一個異步執行入口,程序動態運行時從該入口創建並進入一個異步線程環境,並在該線程執行任務實例及任務實例返回之后的代碼;

3. 一個async函數中聲明多個await關鍵字時,程序將代碼順序創建並進入異步子線程執行任務實例及任務實例返回之后的代碼直到下一個await聲明處, 最后一個await聲明之后的代碼會在最后一個異步子線程中執行 ;

3. await標記的右側代碼返回或定義了一個任務實例,該實例由需要異步執行的目標耗時函數初始化,並在最終定義處觸發異步執行。


免責聲明!

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



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