现在我们知道了异步代码很棒,但是编写异步代码到底有多困难呢?是时候看一下C# 5.0中的async功能了。在第一章 async做了什么一节我们提到了,如果一个方法被标注为async,则需要在方法内使用await关键字。
private async void DumpWebPageAsync(string uri)
{
WebClient webClient = new WebClient();
string page = await webClient.DownloadStringTaskAsync(Console.WriteLine(page);
}
上面例子中的await表达式对方法进行了变换,这种变换使得这个方法成为了异步方法——在下载时方法“暂停”,当下载结束后方法“继续”。在本章中,我们将探讨如何使用这种方式编写异步方法。
使用Async重写Favicon
接下来,我们将修改前边提到的favicon的例子,来学习如何使用async功能。如果条件允许,在继续往下阅读之前,你可以打开之前版本的代码(在default分支上),然后尝试自己添加async和await关键字对其进行修改。
最核心的就是AddAFavicon方法,这个方法负责下载图标文件,然后将图标添加到UI界面上。我们将会把这个方法改造成异步方法,这样UI线程就可以在下载过程中对用户在界面上的操作进行反馈。第一步就使在方法上添加async关键字——就像使用static关键字一样,async需要添加到方法签名中。
然后,我们将使用await关键字等待下载完成。在C#的语义中,await是一元操作符,就像!操作符(取反),以及(type)类型转换符一样。await需要被放在表达式的左侧,意味着以异步的方式等待表达式执行结束。
最后,之前调用DownloadData方法下载数据,这里需要修改为其对应的异步方法DownloadDataTaskAsync。
private async void AddAFavicon(string domain)
{
WebClient webClient = new WebClient();
byte[] bytes = await webClient.DownloadDataTaskAsync("http://" + domain + "/favicon.ico");
Image imageControl = MakeImageControl(bytes);
m_WrapPanel.Children.Add(imageControl);
}
将这个版本的方法与之前看过的两个版本比较,不难发现,这个版本的代码看起来更像之前同步版本的代码——没有额外的方法,只是在同样的结构下额外添加了一点点代码。然而,这个版本的代码与之前第三章提到的方法一样都是异步的。
![]() |
一个带有async的方法不是自动成为异步的,它只是让继续执行其它异步方法变得更容易而已。带async的方法开始也是以同步的方式运行的,直到开始调用异步方法并且进行await,这时它就变为异步的了。 有时,带async的方法也不会await任何方法,在这种情况下它则以同步的方式运行。 |
Task和await
让我们对之前写的await表达式进行分解,下面是WebClient.DownloadStringTaskAsync方法的签名
Task<string> DownloadStringTaskAsync(string address)
返回类型是Task<string>,正如在第三章 Task简介一节所讲的,Task代表一项正在进行中的操作,Task<T>则表示一项正在进行中的操作,并且会在将来某一时刻返回类型为T的结果,所以你可以认为Task<T>确保当耗时操作结束后会返回类型T的结果。
Task和Task<T>都