C# 的 async 與 await 關鍵字

  • 19332
  • 0
  • 2012-07-23

C# 的 async 與 await 關鍵字

在 C# 4.5 5.0 中出現了async 與 await 這兩個關鍵字,我覺得其目的是為了提高使用者與系統之間的互動。比如說,當我們使用一個需要從網路上下載資料的 API 時,若要等到資料都下載完了才反應,如果網路快,那就還好。倘若網路剛好有問題,或是龜速,那使用者鐵定要敲碗了。

我在一開始使用 async 與 awiat 時,我真的還搞不清楚到底是怎麼一回事,常常都無法捉摸他的行為,也就是執行的順序我無法預測 (這樣的 Programmer 真是遭阿 XD)。比如說我有五行程式碼,其順序應該是 1 2 3 4 5。可是,有時候就偏偏給你 2 3 4 1 5。後來才發現這就是非同步的精神。就像 David 老師說的:「如果你要喝咖啡,一定是一邊裝水,一邊到咖啡包,有時候還會一邊攪拌。很少會一步一步來。」(David 老師對於 await 與 async 也有很詳細的介紹) 這個就非常符合我們一般人的行為--非同步 (asynchronous)。

那究竟要怎麼去理解 async 跟 await 呢? 根據 David 老師和我自己的測試結果,得到的結論如下:

 

async

代表該 Method 中可能會用到 await (也就是讓 Compiler 知道有 await,並且在這個地方下斷點)

await

代表這個 Method 為 awaitable 的 Method,也就是說,Compiler 會在這個地方下斷點。

我覺得,到這裡應該還是很模糊,那就來個 Sample Code 吧!!相信一定會清楚很多很多!!!

[要等-必須一步做完才能再做下一步]

   1: private async void Sync_Button_Click(object sender, RoutedEventArgs e) {
   2:     OutputTextBlock.Text += "開始" + Environment.NewLine;
   3:     // 這裡會等 getFileContentAsync() 執行完畢後, 再執行貼上結束字串那一行
   4:     // 因為 Compiler 會再 await 這行下斷點
   5:     OutputTextBlock.Text += await getFileContentAsync();
   6:     OutputTextBlock.Text += "結束" + Environment.NewLine;
   7: }
   8:  
   9: private async Task<string> getFileContentAsync() {
  10:     StorageFolder folder = KnownFolders.DocumentsLibrary;
  11:     StorageFile file = await folder.GetFileAsync(TESTED_FILE_NAME);
  12:     var result = await FileIO.ReadTextAsync(file) + Environment.NewLine;
  13:     return result;
  14: }
輸出結果:
image

[不要等-需要時間的就讓它慢慢做, 不要耽誤使用者的時間]

   1: private void Async_Button_Click(object sender, RoutedEventArgs e) {
   2:     OutputTextBlock.Text += "開始" + Environment.NewLine;
   3:     getFileContent();
   4:     OutputTextBlock.Text += "結束" + Environment.NewLine;
   5: }
   6:  
   7: private async void getFileContent() {
   8:     StorageFolder folder = KnownFolders.DocumentsLibrary;
   9:     StorageFile file = await folder.GetFileAsync(TESTED_FILE_NAME);
  10:     var result = await FileIO.ReadTextAsync(file);
  11:     OutputTextBlock.Text += result + Environment.NewLine;
  12: }

輸出結果:
image

看到其中的差異了嗎? 因為開檔讀檔的數度一定比直接輸出文字慢,所以如果使用非同步的話,那就是 –> 開始—>結束—>檔案中的資料。若是同步,則是—>開始—>檔案中的資料—>結束。

由此就可以更清楚的知道,async只是要告訴 Compiler 這個 Method 裡會用到 await,所以要 Compiler 記得去下斷點。那 awiat 之後的程式碼就是會等 await 那行執行完再執行。如果想要知道更詳細的內容請看 David 老師的說明,非常清楚。

另外,有一點很重要的就是,如果那個 Method 是 awaitable 的話,你又忘記加 await ,那就很可能會收到 __COM_Object 或著 System.Thread[xx] 這類的回傳值,這時就雖然 Compiler 會過,也可以Run,可是值卻是錯的,這點要小心注意才是。

[Reference]

Metro Style App與.NET 4.5中的非同步程式設計概念

A simple example of async and await in C# 5

 

[錯誤更正]

1. C# 應為5.0而非 4.5. (目前應該是 C#5.0, .Net 才是 4.5)