.net 4.5如何使用Async和Await进行异步编程
通过使用异步编程,可避免出现性能瓶颈,并提高应用程序的整体响应。然而,技术编写异步应用程序的传统方法过于复杂,这使得异步程序难以编写,调试和维护。
Visual Studio2012引入了一个简单的开发方法,异步编程,我们可以充分利用.NET Framework 4.5 和 Windows Runtime中对异步的支持。这项复杂的工作将会交由编译器来搞定,开发人员就像是在使用同步代码来编写应用程序的逻辑结构,但其结果是,得到了所有异 步编程的优点,但只要付出一点点工作。
本主题简要介绍何时以及如何使用异步编程,其中包括支持说明本主题的例子。由此下载VS2012.
异步提高响应能力
异步性必不可少,因为现实中有很多潜在的,可阻塞应用程序响应的情况,如当你的应用程序访问网络,文件系统等等。比如访问Web资源,有时是缓慢的 或者是有延迟的。同步处理的话,如果有这样的一个阻塞产生,那么整个应用程序就必须等待。而在一个异步的过程中,应用程序可以先继续进行其他不依赖于网络 的工作,直到所有可能产生阻塞的任务完成后再处理这些任务。
下表显示了典型的可以通过异步编程提高响应的场景。列出的.NET Framework4.5和Windows Runtime的API包含支持异步编程的方法。
应用领域
支持异步方法的API
访问Web
HttpClient , SyndicationClient
使用文件
StorageFile, StreamWriter, StreamReader,XmlReader
使用图像
MediaCapture, BitmapEncoder, BitmapDecoder
WCF程序开发
Synchronous and Asynchronous Operations
使用sockets
Socket
异步性已经被证明对所有通过一个线程访问UI,或是处理UI相关的活动的应用都特别的有价值。如果在同步的应用程序中任何一个处理过程被阻塞,那就意味着所有的东西都被阻塞了。你的应用程将会停止响应,更糟糕的是你可能会得出这样的结论,这只是等待并不是失败。
当你使用异步方法,应用程序将继续响应的UI。您可以调整大小或最小化窗口,例如,当你不想等下去的时候,你可以关闭该应用程序。
现在这种基于异步的方法在你设计异步操作时,就像一组可以选择的自动变速器,也就是说,你可以得到所有之前异步编程的好处,而不必像之前那样苦逼(太让人兴奋了)。
异步方法现在很容易编写
Async和Await关键字是C#异步编程的核心。通过使用这两个关键字,你可以使用.NET Framework或Windows Runtime的资源创建一个异步方法如同你创建一个同步的方法一样容易。通过使用async和await定义的异步方法,这里被称为异步方法。
下面的例子显示了一个异步方法。代码中的几乎所有的东西你看起来应该非常熟悉。注释中描述了你为实现异步操作添加什么功能。
1 // 在签名中三个要注意的事项:
2 // - 该方法具有一个async修饰符.
3 // - 返回类型为 Task or Task<t>. (参考 "返回类型" 一节.)
4 // 这里, 返回值是 Task<int> 因为返回的是一个整数类型.
5 // - 这个方法的名称以 "Async" 结尾.
6 async Task<int> AccessTheWebAsync()
7 {
8 // 你需要添加System.Net.Http的引用来声明client
9 HttpClient client = new HttpClient();
10
11 // GetStringAsync 返回 Task<string>. 这意味着当Task结束等待之后
12 // 你将得到一个string (urlContents).
13 Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
14
15 // 你可以做一些不依赖于 GetStringAsync 返回值的操作.
16 DoIndependentWork();
17
18 // await 操作挂起了当前方法AccessTheWebAsync.
19 // - AccessTheWebAsync 直到getStringTask完成后才会继续.
20 // - 同时, 控制权将返回 AccessTheWebAsync 的调用者.
21 // - 控制权会在getStringTask完成后归还到AccessTheAsync.
22 // - await操作将取回getStringTask中返回的string结果.
23 string urlContents = await getStringTask;
24
25 // return语句用来指定一个整数结果。
26 // 调用AccessTheWebAsync将会收到一个返回值的长度.
27 return urlContents.Length;
28 }
如果AccessTheWebAsync没有什么不依赖于GetStringAsync的内容,也可以直接调用如下代码:
string urlContents = await client.GetStringAsync();
以下几个特点总结了一下前面的例子中的异步方法。
方法中包含了 async 修饰符。
一个async方法按照惯例以“Async”结尾。
返回类型是如下类型之一:
Task<TResult> 当你的方法有返回值时,TResult即返回值的类型
Task 当你的方法没有return语句,或者返回值并不参与任何形式的运算(包括赋值操作)。
Void 当你编写一个异步事件处理时会用到
方法通常包括至少一个await的表达式,这意味着该方法在遇到await时不能继续执行,直到等待异步操作完成。在此期间,该方法将被暂停,并且控制权返回到该方法的调用者。本主题接下来的部分说明了悬挂点后会发生什么。
在异步方法中,我们使用这些已经提供的关键字和类型来表达想要做什么的时候,编译器并没有闲着,他将处理包括跟踪在暂停方法中控制权返回到 await点后将会如何处理。一些常规流程,如循环和异常处理,在之前的异步代码中都比较难以处理。而现在都归结到了一个async方法中,你会感觉你在 写一个同步的代码,之前的那些困惑已经不复存在了。
在异步方法中发生了什么
了解异步编程最重要的是理解控制权是如何在方法之间转移的。下面的图标将会解释这个过程。
图中的数字对应于下面的步骤。
1. 一个事件的处理函数用await的方式调用了异步方法 AccessTheWebAsync。
2. AccessTheWebAsync 创建了一个 HttpClient 实例,并调用了异步方法 GetStringAsync 来以下载一个页面并将内容以string的形式返回.
3. 在GetStringAsync中将会碰到一些让进程挂起的事情。也许它必须等待一个网站下载完成或其他一些阻塞性的动作,为了避免阻塞资源,GetStringAsync把控制权移交给了它的调用者AccessTheWEbAsync。
GetStringAsync返回Task<TResult>泛型,例子中的TResult是string类 型,AccessTheWebAsync将任务交给了getStringTask变量。这个任务代表了一个正在调用GetStringAsync的过程, 与一个承诺,当工作完成时,最终将产生string返回值。
4. 由于getStringTask并没有被(用await)等待着索取结果,so AccessTheWebAsync 可以继续其他不依赖于GetStringAsync最终返回结果的其他任务。这些任务由一个同步方法DoIndenpendentWork代表。
5.DoIndenpendentWork是一个同步方法,将会以同步的方式执行他的工作,并将返回值返回给调用者。
6. AccessTheWebAsync下一步是希望计算已经下载下来的字符长度,但是如果没有这个字符,这个希望也就破灭了。
因此,AccessTheWebAsync 使用了await关键字挂起了自己的线程,并将控制权移交到了AccessTheWebAsync的调用者。AccessTheWebAsync将会返回 一个Task<int>给调用函数。这个任务承诺会在结束是返回给调用者一个int型的返回值。
注意
如果GetStringAsync在AccessTheWebAsync 调用await之前就已经完成了,那么控制权将依然在AccessTheWebAsync中。这种挂起和等待的操作也是很消耗资源的,如果返回值在 await之前就已经得到了,AccessTheWebAsync没必要非得在用等待的方法去得到最终的结果。
在调用方法(这里是一个event的处理函数)的内部,处理过程是叠加的,event的处理函数将会等待AccessTheWebAsync,而 AccessTheWebAsync在等待GetStringAsync,与此同时,调用函数依然可以执行不依赖于这些返回值的操作。
7. GetStringAsync计算并产生一个string结果。这个string结果可能不是按照你现在期望的方式直接返回给他的调用函数,相反,这个结 果保存在一个代表方法完成的任务中,getStringTask。await操作符将会从getStringTask中取回期望的结果。赋值语句将会把结 果交给urlContents。
8. 当AccessTheWebAsync获得了这个字符串结果,我们可以继续计算这个字符串的长度了。这样AccessTheWebAsync的工作也完成了,等待中的event处理函数也可以继续了。
如果你刚刚接触异步编程,那应该花一分钟来思考一下同步行为和异步行为的不同。同步方法在工作完成之后返回(步骤5),但是异步方法返回一个 task值在他工作被暂停的时候(步骤3,6).当异步方法完成了他的工作之后,task被标记为complete,工作的结果也保存在task之中。
异步的API方法
你可能会想知道在哪里可以找到,如GetStringAsync,支持异步编程的API方法。.NET Framework 4.5包含许多使用await和async工作的成员方法。识别这些方法很简单,方法名都是以”Async”结尾的并且返回类型都是Task或者 Task<TResult>。例如,System.IO.Stream类包含的方法,如CopyToAsync,ReadAsync,及 WriteAsync的对应的同步方法是CopyTo,Read和Write。
线程
异步方法的目的是不阻塞操作。在async方法中, await任务在执行的过程中,并不会阻塞当前的线程,其余的方法可以继续执行,控制权将会移交到async方法的调用者。
async和await关键字并不会创建额外的线程,async方法不会去请求多线程操作。真正创建线程的操作是由Task.Run()实现的,一个async方法并不是在他自己的线程上执行的,只有当方法被激活时,才会使用当前线程的上下文和处理时间。
async方法要比BackgroundWorker更实用,而且使用起来更简单而且不用去过多的考虑竞态冲突神马的。async方法会将运行中的代码依据某些算法进行合理的拆分,并传递给线程池,这也是BackgroundWorker不能比的。
Async和Await
如果需要使用async或者await指定一个异步方法,我们需要注意一下两点:
用async标记的异步方应该使用await关键子来制定挂起点。await操作符会告诉编译器,这个async方放在完成之前,后面的代码无法继续执行,同时,控制权转移到async方法的调用者。
标记为async的方法,调用时应使用await。
一个async方法里通常包含一个或多个的对应的await操作符,但如果没有await表达式也不会导致编译错误。但如果调用一个async方 法,却不使用await关键字来标记一个挂起点的话,程序将会忽略async关键字并以同步的方式执行。编译器会对类似的问题发出警告。
async和await都是上下文关键字:更多的细节可以参考:
Async (Visual Basic)
async (C# Reference)
Await Operator (Visual Basic)
await (C# Reference)
返回类型和参数
在.NET Framework编程中,一个async方法通常返回的类型是Task或者Task<TResult>。在异步方法中,await操作符作用于从另外一个异步方法返回的Task。
如果指定Task<TResult>为返回结果,那么这个方法必须包含return指定的TResult结果的语句。
如果使用Task作为返回值,那么这个方法应该不存在使用return语句返回结果的代码,或者返回的结果不参与任何运算(包括赋值操作)。
1 // 明确指定 Task<tresult>
2 async Task<int> TaskOfTResult_MethodAsync()
3 {
4 int hours;
5 // . . .
6 // return一个整数作为结果.
7 return hours;
8 }
9
10 // 调用 TaskOfTResult_MethodAsync
11 Task<int> returnedTaskTResult = TaskOfTResult_MethodAsync();
12 int intResult = await returnedTaskTResult;
13 // 或者使用一条语句
14 int intResult = await TaskOfTResult_MethodAsync();
15
16
17 // 明确指定 Task
18 async Task Task_MethodAsync()
19 {
20 // . . .
21 // 方法没有任何return语句.
22 }
23
24 // 调用 Task_MethodAsync
25 Task returnedTask = Task_MethodAsync();
26 await returnedTask;
27 // 或者使用一条语句
28 await Task_MethodAsync();
每一个返回的task都代表一个正在执行的工作,task包装的信息中包含了这个异步方法的执行时的状态,最终的结果,或者处理过程中抛出的异常。
如果返回值为void,这种类型主要使用于定义事件处理。异步事件通常被认为是一系列异步操作的开始。使用void返回类型不需要await,而且调用void异步方法的函数不会捕获方法抛出的异常。
另外,async方法不能使用ref或者out参数,但是可以调用含有这些参数的方法。
命名约定
按照约定,你应该在异步方法的名称后面追加“Async”用以标记此方法。但是在event,基类和接口中不需要遵守约定,就像本文例子中event处理函数Button1_Click一样。
相关主题
一个完整的例子
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6 using System.Windows;
7 using System.Windows.Controls;
8 using System.Windows.Data;
9 using System.Windows.Documents;
10 using System.Windows.Input;
11 using System.Windows.Media;
12 using System.Windows.Media.Imaging;
13 using System.Windows.Navigation;
14 using System.Windows.Shapes;
15
16
17 using System.Net.Http;
18
19 namespace AsyncFirstExample
20 {
21 public partial class MainWindow : Window
22 {
23 // 将event处理函数用async标记,这样就可以在处理函数中使用await实现异步操作.
24 private async void StartButton_Click(object sender, RoutedEventArgs e)
25 {
26 // 调用和await分离的方式.
27 //Task<int> getLengthTask = AccessTheWebAsync();
28 //// 在这里做一些其他的工作.
29 //int contentLength = await getLengthTask;
30
31 int contentLength = await AccessTheWebAsync();
32
33 resultsTextBox.Text +=
34 String.Format("\r\nLength of the downloaded string: {0}.\r\n", contentLength);
35 }
36
37
38 // 在签名中三个要注意的事项:
39 // - 该方法具有一个async修饰符.
40 // - 返回类型为 Task or Task<t>. (参考 "返回类型" 一节.)
41 // 这里, 返回值是 Task<int> 因为返回的是一个整数类型.
42 // - 这个方法的名称以 "Async" 结尾.
43 async Task<int> AccessTheWebAsync()
44 {
45 // 你需要添加System.Net.Http的引用来声明client
46 HttpClient client = new HttpClient();
47
48 // GetStringAsync 返回 Task<string>. 这意味着当Task结束等待之后
49 // 你将得到一个string (urlContents).
50 Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
51
52 // 你可以做一些不依赖于 GetStringAsync 返回值的操作.
53 DoIndependentWork();
54
55 // await 操作挂起了当前方法AccessTheWebAsync.
56 // - AccessTheWebAsync 直到getStringTask完成后才会继续.
57 // - 同时, 控制权将返回 AccessTheWebAsync 的调用者.
58 // - 控制权会在getStringTask完成后归还到AccessTheAsync.
59 // - await操作将取回getStringTask中返回的string结果.
60 string urlContents = await getStringTask;
61
62 // return语句用来指定一个整数结果。
63 // 调用AccessTheWebAsync将会收到一个返回值的长度.
64 return urlContents.Length;
65 }
66
67
68 void DoIndependentWork()
69 {
70 resultsTextBox.Text += "Working . . . . . . .\r\n";
71 }
72 }
73 }
74
75 // 运行结果:
76
77 // Working . . . . . . .
78
79 // Length of the downloaded string: 41564.
.net 4.5如何使用Async和Await进行异步编程的更多相关文章
- 使用 Async 和 Await 的异步编程(C# 和 Visual Basic)[msdn.microsoft.com]
看到Microsoft官方一篇关于异步编程的文章,感觉挺好,不敢独享,分享给大家. 原文地址:https://msdn.microsoft.com/zh-cn/library/hh191443.asp ...
- 【转】【C#】C# 5.0 新特性——Async和Await使异步编程更简单
一.引言 在之前的C#基础知识系列文章中只介绍了从C#1.0到C#4.0中主要的特性,然而.NET 4.5 的推出,对于C#又有了新特性的增加--就是C#5.0中async和await两个关键字,这两 ...
- 使用Async和Await进行异步编程(C#版 适用于VS2015)
你可以使用异步编程来避免你的应用程序的性能瓶颈并且加强总体的响应.然而,用传统的技术来写异步应用是复杂的,同时编写,调试和维护都很困难. VS2012介绍了简单的方法,那就是异步编程,它在.Net F ...
- 使用Async和Await进行异步编程(C#版 适用于VS2015) z
你可以使用异步编程来避免你的应用程序的性能瓶颈并且加强总体的响应.然而,用传统的技术来写异步应用是复杂的,同时编写,调试和维护都很困难. VS2012介绍了简单的方法,那就是异步编程,它在.Net F ...
- Async和Await进行异步编程
使用Async和Await进行异步编程(C#版 适用于VS2015) 你可以使用异步编程来避免你的应用程序的性能瓶颈并且加强总体的响应.然而,用传统的技术来写异步应用是复杂的,同时编写,调试和维护都很 ...
- 【C#复习总结】 Async 和 Await 的异步编程
谈到异步,必然要说下阻塞,在知乎上看到了网友举的例子非常省动,在这里我引用下. 怎样理解阻塞非阻塞与同步异步的区别? 老张爱喝茶,废话不说,煮开水. 出场人物:老张,水壶两把(普通水壶,简称水壶:会响 ...
- 转:[你必须知道的异步编程]C# 5.0 新特性——Async和Await使异步编程更简单
本专题概要: 引言 同步代码存在的问题 传统的异步编程改善程序的响应 C# 5.0 提供的async和await使异步编程更简单 async和await关键字剖析 小结 一.引言 在之前的C#基础知 ...
- [你必须知道的异步编程]C# 5.0 新特性——Async和Await使异步编程更简单
本专题概要: 引言 同步代码存在的问题 传统的异步编程改善程序的响应 C# 5.0 提供的async和await使异步编程更简单 async和await关键字剖析 小结 一.引言 在之前的C#基础知 ...
- Async 和 Await 的异步编程 资料汇总
使用 Async 和 Await 的异步编程 https://msdn.microsoft.com/zh-cn/library/hh191443(v=vs.120).aspx 异步程序中的控制流 ht ...
- 四、C# 5.0 新特性——Async和Await使异步编程更简单
一.引言 .NET 4.5 的推出,对于C#又有了新特性的增加--就是C#5.0中async和await两个关键字,这两个关键字简化了异步编程,之所以简化了,还是因为编译器给我们做了更多的工作,下面就 ...
随机推荐
- leetcode 之Remove Duplicates from Sorted List(17)
很简单的一题,需要注意的是如果某结点重复了记得将其删除. ListNode *deleteDuplicates(ListNode *head) { if (head == nullptr) retur ...
- leetcode 之Gas Station(11)
这题的思路很巧妙,用两个变量,一个变量衡量当前指针是否有效,一个衡量整个数组是否有解,需要好好体会. int gasStation(vector<int> &gas, vector ...
- P2885
2885 code[class*="language-"] { padding: .1em; border-radius: .3em; white-space: normal; b ...
- POJ 1984 Navigation Nightmare(二维带权并查集)
题目链接:http://poj.org/problem?id=1984 题目大意:有n个点,在平面上位于坐标点上,给出m关系F1 F2 L D ,表示点F1往D方向走L距离到点F2,然后给出一系 ...
- QQ分享 QQ空间分享 API链接:
QZone: "http://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?url={{URL}}&title={{TITL ...
- nodejs调试基础【转载】
nodejs调试基础[转载] 看到了一片不错的文章: 作者:前端求生之路 链接:nodejs调试基础[转载]
- jquery.validate验证表单配合回调提交和h5.storage本地保存笔记
表单验证插件我使用:jquery.validate.js 支持中文提示,可扩展性强!教程地址 本地保存状态信息使用:h5提供的storage,浏览器支持5m的存储量,存储类型必须是string类型,并 ...
- LOJ #6278. 数列分块入门 2-分块(区间加法、查询区间内小于某个值x的元素个数)
#6278. 数列分块入门 2 内存限制:256 MiB时间限制:500 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: hzwer 提交提交记录统计测试数据讨论 6 题目描述 给出 ...
- thinkphp下实现ajax无刷新分页
1.前言 作为一名php程序员,我们开发网站主要就是为了客户从客户端进行体验,在这里,thinkphp框架自带的分页类是每次翻页都要刷新一下整个页面,这种翻页的用户体验显然是不太理想的,我们希望每次翻 ...
- CNN的发展
模型的建立过程: 1959年,Hubel & Wiesel发现动物视觉皮层中的细胞负责检测感受野(receptive fields)中的光线.论文:Receptive fields and f ...