C# 的 async 与 await 关键字

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 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)