C#多線程編程(2)-- async,await基本用法


上一章我簡單介紹了異步編程的基本方法,推薦使用的方式是Task。Task是對線程池的封裝,並且可以對Task使用async和await關鍵字。這兩個關鍵字的使用非常簡單,那么這兩個關鍵字究竟起什么作用?工作原理是怎樣的?本文就來簡單解釋。

本系列是我讀《CLR via C#》的總結,但是書中關於async和await關鍵字的講解不是很多。其中28.3小節通過簡單例子以及IL反編譯的方式,講解了編譯器如何將異步函數編譯成狀態機,雖然反編譯出的代碼中作者添加了大量的注釋,無奈本人能力有限,很多底層代碼不能很好的理解,因此我通過小例子的方式反復的嘗試,加上書中的講解,我想我已經基本掌握了async和await的使用方式和基本原理。若您有興趣了解底層代碼,可以找來《CLR via C#》的28.2和28.3章節來讀一讀。

上一章節已經講了,當你想要利用另一個線程來執行一段程序,推薦的方式是Task,我們看個例子:

 public async void RunAsync(){
            var t = await Task.Run(() =>
            {
//模擬其他操作 Thread.Sleep(
2000); return "task finished"; }); Console.WriteLine(t); }

運行代碼,可以看到程序在大約2秒后,控制台會打印出"task finished",且在這過程中,主線程沒有被Sleep(阻塞),RunAsync后面的方法可以繼續執行,如

1 static void Main(string[] args) {
2             RunAsync();
3             Console.WriteLine("123");
4             Console.Read();
5 }

控制台會先顯示123,過大約2秒后,顯示task finished。程序在執行RunAsync()后,跳過了該方法,直接執行了Console.WriteLine("123")。可以看到RunAsync方法的簽名中添加了async關鍵字,在Task.Run()前面添加了await關鍵字,這兩個關鍵字的作用是表示RunAsync方法在執行到await關鍵字后,會將該方法的其余部分封裝成一個委托,該委托會在Task.Run()返回的task執行完成后,執行該委托(具體編譯器如何將該方法轉換成狀態機,並在任務結束后,再繼續執行該委托,可以看《CLR via C#》的28.3章節)。

 1  public async void RunAsync(){
 2             //其他操作
 3             //當遇到await,會將后面的程序封裝成一個委托
 4             var t = await Task.Run(() =>
 5             {
 6                 Thread.Sleep(2000);
 7                 return "task finished";
 8             });
 9             //這里往后的代碼被封裝成委托,當t.IsComplete后,才會被執行
10             //其他操作
11             Console.WriteLine(t);
12         }

關鍵字async表明該方法是一個異步方法,await關鍵字只允許在標有async的方法中使用。當異步方法具有返回值時,調用該異步方法的函數也要添加async關鍵字,並在調用方法處添加await,不然會造成異步失效。

 1  static void Main(string[] args){
 2             Console.WriteLine(RunAsync().Result);
 3             Console.WriteLine("Async Run");
 4             Console.Read();
 5         }
 6         public static async Task<string> RunAsync(){
 7             return await Task.Run(() =>
 8             {
 9                 Thread.Sleep(2000);
10                 return "task finished";
11             });
12         }

運行,程序在等待大約2秒后,顯示task finished,然后再顯示Async Run,這是因為雖然RunAsync方法異步返回,但是主線程一直在等待RunAsync的結果,除此之外什么也不干,這樣當然是不好。

 1 static void Main(string[] args){
 2    TestAsync();
 3    Console.WriteLine("Async Run");
 4    Console.Read();
 5 }
 6 public static async Task<string> RunAsync(){
 7     return await Task.Run(() =>
 8     {
 9         Thread.Sleep(2000);
10         return "task finished";
11     });
12 }
13 public static async void TestAsync(){
14     Console.WriteLine(await RunAsync());
15 }

Main函數無法添加async關鍵字,因此我用TestAsync方法包裝了一下,可以看到添加了await 和async關鍵字后,程序會先出現Async Run,然后是task finished。即在異步函數的調用中,若函數包含返回值,則應通過添加await的方式調用異步函數,這樣可以做到接着異步,而不是半途而廢的異步。

在循環中也可以添加await關鍵字

 1 static void Main(string[] args)
 2 {
 3     TestAsync();
 4     Console.WriteLine("Async Run");
 5     Console.Read();
 6 }
 7 public static async Task<string> RunAsync()
 8 {
 9     return await Task.Run(() =>
10     {
11         Thread.Sleep(2000);
12         return "task finished";
13     });
14 }
15 public static async void TestAsync()
16 {
17     for (int i = 0; i < 4; i++)
18     {
19         Console.WriteLine(await RunAsync() + i);
20     }
21 }

上述例子會以你希望的那樣,先顯示Async Run,然后依次打印task finished0 - task finished4,且每次打印間歇大約2秒。

以上就是Task與 async 和 await 關鍵字的使用以及基本原理。使用await關鍵字,該關鍵字之后的邏輯都會被封裝到一個委托,等到任務執行結束后,再調用當前線程繼續執行該委托。那么能夠調用當前線程,也應該有方法當任務執行結束后,繼續調用線程池來執行方法。該部分C#多線程編程(3)會有講解。歡迎有問題的小伙伴和我在評論區交流。


免責聲明!

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



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