引言
同步代码存在的问题
// 单击事件 private void btnClick_Click(object sender, EventArgs e) { this.btnClick.Enabled = false; long length = AccessWeb(); this.btnClick.Enabled = true; // 这里可以做一些不依赖回复的操作 OtherWork(); this.richTextBox1.Text += String.Format("\n 回复的字节长度为: {0}.\r\n", length); txbMainThreadID.Text = Thread.CurrentThread.ManagedThreadId.ToString(); } private long AccessWeb() { MemoryStream content = new MemoryStream(); // 对MSDN发起一个Web请求 HttpWebRequest webRequest = WebRequest.Create("http://msdn.microsoft.com/zh-cn/") as HttpWebRequest; if (webRequest != null) { // 返回回复结果 using (WebResponse response = webRequest.GetResponse()) { using (Stream responseStream = response.GetResponseStream()) { responseStream.CopyTo(content); } } } txbAsynMethodID.Text = Thread.CurrentThread.ManagedThreadId.ToString(); return content.Length; }
运行程序后,当我们点击窗体的 “点击我”按钮之后,在得到服务器响应之前,我们不能对窗体进行任何的操作,包括移动窗体,关闭窗体等,具体运行结果如下:
传统的异步编程来改善程序的响应
private void btnClick_Click(object sender, EventArgs e) { this.richTextBox1.Clear(); btnClick.Enabled = false; AsyncMethodCaller caller = new AsyncMethodCaller(TestMethod); IAsyncResult result = caller.BeginInvoke(GetResult, null); //// 捕捉调用线程的同步上下文派生对象 //sc= SynchronizationContext.Current; } # region 使用APM实现异步编程 // 同步方法 private string TestMethod() { // 模拟做一些耗时的操作 // 实际项目中可能是读取一个大文件或者从远程服务器中获取数据等。 for (int i = 0; i < 10; i++) { Thread.Sleep(200); } return "点击我按钮事件完成"; } // 回调方法 private void GetResult(IAsyncResult result) { AsyncMethodCaller caller = (AsyncMethodCaller)((AsyncResult)result).AsyncDelegate; // 调用EndInvoke去等待异步调用完成并且获得返回值 // 如果异步调用尚未完成,则 EndInvoke 会一直阻止调用线程,直到异步调用完成 string resultvalue = caller.EndInvoke(result); //sc.Post(ShowState,resultvalue); richTextBox1.Invoke(showStateCallback, resultvalue); } // 显示结果到richTextBox private void ShowState(object result) { richTextBox1.Text = result.ToString(); btnClick.Enabled = true; } // 显示结果到richTextBox //private void ShowState(string result) //{ // richTextBox1.Text = result; // btnClick.Enabled = true; //} #endregion
运行的结果为:
C# 5.0 提供的async和await使异步编程更简单
private async void btnClick_Click(object sender, EventArgs e) { long length = await AccessWebAsync(); // 这里可以做一些不依赖回复的操作 OtherWork(); this.richTextBox1.Text += String.Format("\n 回复的字节长度为: {0}.\r\n", length); txbMainThreadID.Text = Thread.CurrentThread.ManagedThreadId.ToString(); } // 使用C# 5.0中提供的async 和await关键字来定义异步方法 // 从代码中可以看出C#5.0 中定义异步方法就像定义同步方法一样简单。 // 使用async 和await定义异步方法不会创建新线程, // 它运行在现有线程上执行多个任务. // 此时不知道大家有没有一个疑问的?在现有线程上(即UI线程上)运行一个耗时的操作时, // 为什么不会堵塞UI线程的呢? // 这个问题的答案就是 当编译器看到await关键字时,线程会 private async Task<long> AccessWebAsync() { MemoryStream content = new MemoryStream(); // 对MSDN发起一个Web请求 HttpWebRequest webRequest = WebRequest.Create("http://msdn.microsoft.com/zh-cn/") as HttpWebRequest; if (webRequest != null) { // 返回回复结果 using (WebResponse response = await webRequest.GetResponseAsync()) { using (Stream responseStream = response.GetResponseStream()) { await responseStream.CopyToAsync(content); } } } txbAsynMethodID.Text = Thread.CurrentThread.ManagedThreadId.ToString() ; return content.Length; } private void OtherWork() { this.richTextBox1.Text += "\r\n等待服务器回复中.................\n"; }
运行结果如下:
答: 不会,被async关键字标识的方法不会影响方法是同步还是异步运行并完成,而是,它使方法可被分割成多个片段,其中一些片段可能异步运行,这样这个方法可能异步完成。这些片段界限就出现在方法内部显示使用”await”关键字的位置处。所以,如果在标记了”async”的方法中没有显示使用”await”,那么该方法只有一个片段,并且将以同步方式运行并完成。在await关键字出现的前面部分代码和后面部分代码都是同步执行的(即在调用线程上执行的,也就是GUI线程,所以不存在跨线程访问控件的问题),await关键处的代码片段是在线程池线程上执行。总结为——使用async和await关键字实现的异步方法,此时的异步方法被分成了多个代码片段去执行的,而不是像之前的异步编程模型(APM)和EAP那样,使用线程池线程去执行一整个方法。

