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: }
輸出結果:
[不要等-需要時間的就讓它慢慢做, 不要耽誤使用者的時間]
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: }
輸出結果:
看到其中的差異了嗎? 因為開檔讀檔的數度一定比直接輸出文字慢,所以如果使用非同步的話,那就是 –> 開始—>結束—>檔案中的資料。若是同步,則是—>開始—>檔案中的資料—>結束。
由此就可以更清楚的知道,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)