经过两天密集型的学习,翻阅了大量 webpages ,点击了不少重点 blogs,总算基本了解了一些 async/await 搭配使用的入门技巧,总结一下
1. async/await 应该只是语法上的甜点,让想使用异步方法运行程序的程序员能够专心专注代码逻辑,而别被原来的 Begin... End... 型或是IAsync ...Async 的老式异步带的越来越远。想起刚试着编写 Tcp Listener的时候,真是噩梦。异步响应必须放在另一个回调方法或是事件中,而从异步响应产生的异步接收又必须再次放到另一个回调方法或是事件中,更可怕的是,如何循环它们。
2. async/await 标识的方法,“本身不主动创建额外线程”——这句话很容易让人误解。《Async 和 Await 异步编程的原理》这篇文章写的比较详细,异步,必然多线程,更何况是从 UI 线程启动的。只是这种多线程被Framework透明掉了,使用者不必自己在这方面去走脑子怎么创建线程,怎么回收,怎么捕捉异常,还得让代码好看一点。
3. await 让我开始的时候陷入了一个误区,认为使用 var result = await FunRun(...) 这种写法,FunRun()方法就会异步运行而不会阻塞UI线程,这是错的,我忽略了一个很严重的问题。先看一下原来错误的代码:
1 private async Task<TimeSpan> Download(Uri address, string fileName) 2 { 3 this.uri = address; 4 this.fileName = fileName; 5 DateTime start = DateTime.Now; 6 // 7 HttpWebRequest request; 8 HttpWebResponse response = null; 9 try 10 { 11 request = (HttpWebRequest)WebRequest.Create(address); 12 response = (HttpWebResponse)request.GetResponse(); 13 using (Stream httpStream = response.GetResponseStream()) 14 { 15 using (FileStream writer = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read)) 16 { 17 byte[] buffer = new byte[8192]; 18 int readLength = httpStream.Read(buffer, 0, buffer.Length); 19 // 20 while (readLength > 0) 21 { 22 writer.Write(buffer, 0, readLength); 23 readLength = httpStream.Read(buffer, 0, buffer.Length); 24 } 25 } 26 } 27 } 28 catch(Exception) 29 { 30 if (response != null) { response.Close(); } 31 } 32 33 return DateTime.Now - start; 34 }
代码完成下载一个资源到指定位置的功能,本身逻辑没有错误,看起来这似乎是一个标准的带有 async 标记和 Task<T> 返回类型的方法,所以我信心慢慢的调用了它。
1 private async void button1_Click(object sender, EventArgs e) 2 { 3 //略过部分逻辑判断的东西 4 ...... 5 6 TestApp.HttpDownloader.HttpDownloaderEngine hdEngine = new HttpDownloaderEngine(); 7 var time = await hdEngine.Download(address, saveFileTextBox.Text); 8 MessageBox.Show(string.Format("下载完成,共耗时 {0} 秒", time.Seconds.ToString())); 9 }