[.NET] 利用 async & await 的异步编程
利用 async & await 的异步编程
【博主】反骨仔 【出处】http://www.cnblogs.com/liqingwen/p/5922573.html
目录
一、异步编程的简介
通过使用异步编程,你可以避免性能瓶颈并增强你的应用程序的总体响应能力。
从 VS 2012 开始,新引入了一个简化的方法,称为异步编程。我们在 >= .NET 4.5 中和 Windows 运行时中使用异步,编译器它会帮助了我们降低了曾经进行的高难度异步代码编写的工作,但逻辑结构却类似于同步代码。因此,我们仅需要进行一小部分编程的工作就可以获得异步编程的所有优点。
二、异步提高响应能力
异步对可能引起阻塞的活动(如访问 Web 时),对 Web 资源的访问有时过慢或延迟过高。若这种任务在同步过程中受阻,则整个应用程序必须等待响应完成。 在使用异步的过程中,我们的应用程序可继续执行不依赖 Web 资源的其他工作,并会一直等待阻塞的任务顺利完成。
这是一些典型的使用异步的应用场景,以及一些在 .NET >= 4.5 后新增的类库。
所有与用户界面相关的操作通常共享一个线程,所以使用异步对于使用 UI 线程的 App 来说是非常重要的。
如果说你的 App 所有操作都是同步的,也就是说,当一个线程出现阻塞,其它线程都会出现阻塞,更严重的是, App 会停止响应。
使用异步方法时,App 将继续响应 UI。如:最大和最小化,但是功能依然在后台执行(如:下载)。
三、更容易编写的异步方法
C# 中的 async 和 await 关键字都是异步编程的核心。通过使用这两个关键字,我们就可以在 .NET 轻松创建异步方法。
示例:
- /// <summary>
- /// 异步访问 Web
- /// </summary>
- /// <returns></returns>
- /// <remarks>
- /// 方法签名的 3 要素:
- /// ① async 修饰符
- /// ② 返回类型 Task 或 Task<TResult>:这里的 Task<int> 表示 return 语句返回 int 类型
- /// ③ 方法名以 Async 结尾
- /// </remarks>
- async Task<int> AccessTheWebAsync()
- {
- //记得 using System.Net.Http 哦
- var client = new HttpClient();
- //执行异步方法 GetStringAsync
- Task<string> getStringTask = client.GetStringAsync("http://www.google.com.hk/");
- //假设在这里执行一些非异步的操作
- Do();
- //等待操作挂起方法 AccessTheWebAsync
- //直到 getStringTask 完成,AccessTheWebAsync 方法才会继续执行
- //同时,控制将返回到 AccessTheWebAsync 方法的调用方
- //直到 getStringTask 完成后,将在这里恢复控制。
- //然后从 getStringTask 拿到字符串结果
- string urlContents = await getStringTask;
- //返回字符串的长度(int 类型)
- return urlContents.Length;
- }
如果 AccessTheWebAsync 在调用 GetStringAsync() 时没有其它操作(如:代码中的 Do()),你可以用这样的方式来简化代码。
- string urlContents = await client.GetStringAsync("http://www.google.com.hk/");
简单总结:
(1)方法签名包含一个 async 修饰符。
(2)根据约定,异步方法的名称需要以“Async”后缀为结尾。
(3)3 种返回类型:
① Task<TResult>:返回 TResult 类型。
② Task:没有返回值,即返回值为 void。
③ void:只适用于异步事件处理程序。
(4)方法通常包含至少一个 await 表达式,该表达式标记一个点,我们可以成为悬挂点,在该点上,直到等待的异步操作完成,之后的方法才能继续执行。 与此同时,该方法将挂起,并将控制权返回到方法的调用方。
需要使用异步方法的话,我们直接在系统内部使用所提供的关键字 async 和 await 就可以了,剩余的其它事情,就留给编译器吧。
四、异步方法的控制流(核心)
异步编程中最重要却不易懂的是控制流,即不同方法间的切换。现在,请用一颗感恩的心来观察下图。
步骤解析:
① 事件处理程序调用并等待 AccessTheWebAsync() 异步方法。
② AccessTheWebAsync 创建 HttpClient 对象并调用它的 GetStringAsync 异步方法来下载网站内容。
③ 假设 GetStringAsync 中发生了某种情况,该情况挂起了它的进程。可能必须等待网站下载或一些其他阻塞的活动。为避免阻塞资源,GetStringAsync() 会将控制权出让给其调用方 AccessTheWebAsync。GetStringAsync 返回 Task,其中 TResult 为字符串,并且 AccessTheWebAsync 将任务分配给 getStringTask 变量。该任务表示调用 GetStringAsync 的正在进行的进程,其中承诺当工作完成时产生实际字符串值。
④ 由于尚未等待 getStringTask,因此,AccessTheWebAsync 可以继续执行不依赖于 GetStringAsync 得出最终结果的其他任务。该任务由对同步方法 DoIndependentWork 的调用表示。
⑤ DoIndependentWork 是完成其工作并返回其调用方的同步方法。
⑥ AccessTheWebAsync 已完成工作,可以不受 getStringTask 的结果影响。 接下来,AccessTheWebAsync 需要计算并返回该下载字符串的长度,但该方法仅在具有字符串时才能计算该值。因此,AccessTheWebAsync 使用一个 await 运算符来挂起其进度,并把控制权交给调用 AccessTheWebAsync 的方法。AccessTheWebAsync 将 Task<int> 返回至调用方。 该任务表示对产生下载字符串长度的整数结果的一个承诺。
在调用方内部(假设这是一个事件处理程序),处理模式将继续。在等待结果前,调用方可以开展不依赖于 AccessTheWebAsync 结果的其他工作,否则就需等待片刻。事件处理程序等待 AccessTheWebAsync,而 AccessTheWebAsync 等待 GetStringAsync。
⑦ GetStringAsync 完成并生成一个字符串结果。 字符串结果不是通过你预期的方式调用 GetStringAsync 所返回的。(请记住,此方法已在步骤 3 中返回一个任务。)相反,字符串结果存储在表示完成方法 getStringTask 的任务中。 await 运算符从 getStringTask 中检索结果。赋值语句将检索到的结果赋给 urlContents。
⑧ 当 AccessTheWebAsync 具有字符串结果时,该方法可以计算字符串长度。然后,AccessTheWebAsync 工作也将完成,并且等待事件处理程序可继续使用。
你可以尝试思考一下同步行为和异步行为之间的差异。当其工作完成时(第 5 步)会返回一个同步方法,但当其工作挂起时(第 3 步和第 6 步),异步方法会返回一个任务值。在异步方法最终完成其工作时,任务会标记为已完成,而结果(如果有)将存储在任务中。
五、异步中的线程
async 和 await 关键字不会导致创建其他线程。因为异步方法不会在其自身线程上运行,因此它不需要多线程。只有当方法处于活动状态时,该方法将在当前同步上下文中运行并使用线程上的时间。可以使用 Task.Run 将占用大量 CPU 的工作移到后台线程,但是后台线程不会帮助正在等待结果的进程变为可用状态。
对于异步编程而言,该基于异步的方法优于几乎每个用例中的现有方法。具体而言,此方法比 BackgroundWorker 更适用于 IO 绑定的操作,因为此代码更简单且无需防止抢先争用条件。结合 Task.Run() 使用时,异步编程比 BackgroundWorker 更适用于 CPU 绑定的操作,因为异步编程将运行代码的协调细节与 Task.Run 传输至线程池的工作区分开来。
六、async 和 await 修饰符
可以使用 await 来指定悬挂点。await 运算符会告诉编译器,异步方法只有直到等待的异步过程执行完成,才能继续通过该点往下执行。同时,控制权将返回至异步方法的调用方。await 表达式中异步方法在挂起后,如果该方法还没有执行完成并退出,finally 块中的将不会执行。
标记的异步方法本身可以通过调用它的方法进行等待。异步方法中通常包含一个或多个 await 运算符,当然,一个 await 表达式都不存在也不会导致编译器错误,但是编译器会发出警告,该方法在执行的时候依然会依照同步方法来执行,async 其实只是一个标识的作用而已,告诉编译器他“应该”是一个异步方法。
七、返回类型和参数信息
示例:
- static async Task<Guid> Method1Async() //Task<Guid>
- {
- var result = Guid.NewGuid();
- await Task.Delay();
- //这里返回一个 Guid 的类型
- return result;
- }
- static async Task Method2Async() //Task
- {
- //Do...
- await Task.Delay();
- //Do...
- //这里没有 return 语句
- }
- //调用 Method1Async
- //方式一
- Task<Guid> t1 = Method1Async();
- Guid guid1 = t1.Result;
- //方式二
- Guid guid2 = await Method1Async();
- //调用 Method2Async
- //方式一
- Task t2 = Method2Async();
- await t2;
- //方式二
- await Method2Async();
每个返回的任务表示正在进行的工作。任务可封装有关异步进程状态的信息,如果未成功,则最后会封装来自进程的最终结果,或者是由该进程引发的异常。
【疑问】那么 void 返回类型是在什么情况下才使用的呢?
主要用于异步的事件处理程序,异步事件处理程序通常作为异步程序的起始点。void 返回类型告诉了编译器,无需对他进行等待,并且,对于 void 返回类型的方法,我们也无法对他进行异常的捕捉。
异步方法不能够在参数中声明与使用 ref 和 out 关键字,但是异步方法可以调用包含这些参数的方法。
八、命名的约定
传送门
【参考引用】微软官方文档图片
【参考】https://msdn.microsoft.com/zh-cn/library/windows/apps/hh191443(v=vs.110).aspx
[.NET] 利用 async & await 的异步编程的更多相关文章
- 利用 async & await 的异步编程
走进异步编程的世界 - 开始接触 async/await 利用 async & await 的异步编程 async 的三大返回类型 公司技术需求备忘录
- [.NET] 利用 async & await 进行异步 IO 操作
利用 async & await 进行异步 IO 操作 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6082673.html 序 上次,博主 ...
- Atitit. Async await 优缺点 异步编程的原理and实现 java c# php
Atitit. Async await 优缺点 异步编程的原理and实现 java c# php 1. async & await的来源1 2. 异步编程history1 2.1. 线程池 2 ...
- 用 Python 3 的 async / await 做异步编程
前年我曾写过一篇<初探 Python 3 的异步 IO 编程>,当时只是初步接触了一下 yield from 语法和 asyncio 标准库.前些日子我在 V2EX 看到一篇<为什么 ...
- 使用 async/ await 进行 异步 编程
一.异步函数 异步函数概念. 通常 是 指用 async 修饰 符 声明 的, 可 包含 await 表达式 的 方法 或 匿名 函数 1. 从 语言 的 视角 来看, 这些 await 表达式 正是 ...
- 使用 Async 和 Await 的异步编程(C# 和 Visual Basic)[msdn.microsoft.com]
看到Microsoft官方一篇关于异步编程的文章,感觉挺好,不敢独享,分享给大家. 原文地址:https://msdn.microsoft.com/zh-cn/library/hh191443.asp ...
- 使用Async和Await进行异步编程(C#版 适用于VS2015)
你可以使用异步编程来避免你的应用程序的性能瓶颈并且加强总体的响应.然而,用传统的技术来写异步应用是复杂的,同时编写,调试和维护都很困难. VS2012介绍了简单的方法,那就是异步编程,它在.Net F ...
- 使用Async和Await进行异步编程(C#版 适用于VS2015) z
你可以使用异步编程来避免你的应用程序的性能瓶颈并且加强总体的响应.然而,用传统的技术来写异步应用是复杂的,同时编写,调试和维护都很困难. VS2012介绍了简单的方法,那就是异步编程,它在.Net F ...
- Async和Await进行异步编程
使用Async和Await进行异步编程(C#版 适用于VS2015) 你可以使用异步编程来避免你的应用程序的性能瓶颈并且加强总体的响应.然而,用传统的技术来写异步应用是复杂的,同时编写,调试和维护都很 ...
随机推荐
- 谈谈一些有趣的CSS题目(十二)-- 你该知道的字体 font-family
开本系列,谈谈一些有趣的 CSS 题目,题目类型天马行空,想到什么说什么,不仅为了拓宽一下解决问题的思路,更涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果解题 ...
- 编写高质量代码:改善Java程序的151个建议(第5章:数组和集合___建议75~78)
建议75:集合中的元素必须做到compareTo和equals同步 实现了Comparable接口的元素就可以排序,compareTo方法是Comparable接口要求必须实现的,它与equals方法 ...
- 哪种缓存效果高?开源一个简单的缓存组件j2cache
背景 现在的web系统已经越来越多的应用缓存技术,而且缓存技术确实是能实足的增强系统性能的.我在项目中也开始接触一些缓存的需求. 开始简单的就用jvm(java托管内存)来做缓存,这样对于单个应用服务 ...
- UWP中新加的数据绑定方式x:Bind分析总结
UWP中新加的数据绑定方式x:Bind分析总结 0x00 UWP中的x:Bind 由之前有过WPF开发经验,所以在学习UWP的时候直接省略了XAML.数据绑定等几个看着十分眼熟的主题.学习过程中倒是也 ...
- .Net多线程编程—预备知识
1 基本概念 共享内存的多核架构:一个单独的封装包内封装了多个互相连接的未处理器,且所有内核都可以访问主内存.共享内存的多核系统的一些微架构,例如内核暂停功能,超频. 内核暂停功能:当使用内核不多的时 ...
- Vue-Router 页面正在加载特效
Vue-Router 页面正在加载特效 如果你在使用 Vue.js 和 Vue-Router 开发单页面应用.因为每个页面都是一个 Vue 组件,你需要从服务器端请求数据,然后再让 Vue 引擎来渲染 ...
- CoreCRM 开发实录——Travis-CI 实现 .NET Core 程度在 macOS 上的构建和测试 [无水干货]
上一篇文章我提到:为了使用"国货",我把 Linux 上的构建和测试委托给了 DaoCloud,而 Travis-CI 不能放着不用啊.还好,这货支持 macOS 系统.所以就把 ...
- ASP.NET Core应用针对静态文件请求的处理[5]: DefaultFilesMiddleware中间件如何显示默认页面
DefaultFilesMiddleware中间件的目的在于将目标目录下的默认文件作为响应内容.我们知道,如果直接请求的就是这个默认文件,那么前面介绍的StaticFileMiddleware中间件会 ...
- 从零开始编写自己的C#框架(27)——什么是开发框架
前言 做为一个程序员,在开发的过程中会发现,有框架同无框架,做起事来是完全不同的概念,关系到开发的效率.程序的健壮.性能.团队协作.后续功能维护.扩展......等方方面面的事情.很多朋友在学习搭建自 ...
- (翻译)FIFO In Hardware
翻译一些自己觉得有价值的材料,工作中碰到英语大多数是读,基本没有写或者翻的,翻得不好不到位的敬请指摘. 同时也附原文以供参考. http://electronics.stackexchange.com ...