利用 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> 返回至调用方。 该任务表示对产生下载字符串长度的整数结果的一个承诺。

  【备注】如果 GetStringAsync(即 getStringTask)在 AccessTheWebAsync 等待前完成,则控制权会保留在 AccessTheWebAsync 中。 如果异步调用过程 (getStringTask) 已完成,并且 AccessTheWebSync 不必等待最终结果,则挂起然后返回到 AccessTheWebAsync,但这会造成成本的浪费。

  在调用方内部(假设这是一个事件处理程序),处理模式将继续。在等待结果前,调用方可以开展不依赖于 AccessTheWebAsync 结果的其他工作,否则就需等待片刻。事件处理程序等待 AccessTheWebAsync,而 AccessTheWebAsync 等待 GetStringAsync。

  ⑦ GetStringAsync 完成并生成一个字符串结果。 字符串结果不是通过你预期的方式调用 GetStringAsync 所返回的。(请记住,此方法已在步骤 3 中返回一个任务。)相反,字符串结果存储在表示完成方法 getStringTask 的任务中。 await 运算符从 getStringTask 中检索结果。赋值语句将检索到的结果赋给 urlContents。

  ⑧ 当 AccessTheWebAsync 具有字符串结果时,该方法可以计算字符串长度。然后,AccessTheWebAsync 工作也将完成,并且等待事件处理程序可继续使用。

  你可以尝试思考一下同步行为和异步行为之间的差异。当其工作完成时(第 5 步)会返回一个同步方法,但当其工作挂起时(第 3 步和第 6 步),异步方法会返回一个任务值。在异步方法最终完成其工作时,任务会标记为已完成,而结果(如果有)将存储在任务中。

五、异步中的线程

  异步方法旨在成为非阻塞操作。异步方法中的 await 表达式在等待的任务执行的同时不会阻塞当前线程。相反,await 表达式在继续执行时方法的其余部分并将控制权返回到异步方法的调用方。

  async 和 await 关键字不会导致创建其他线程。因为异步方法不会在其自身线程上运行,因此它不需要多线程。只有当方法处于活动状态时,该方法将在当前同步上下文中运行并使用线程上的时间。可以使用 Task.Run 将占用大量 CPU 的工作移到后台线程,但是后台线程不会帮助正在等待结果的进程变为可用状态。

  对于异步编程而言,该基于异步的方法优于几乎每个用例中的现有方法。具体而言,此方法比 BackgroundWorker 更适用于 IO 绑定的操作,因为此代码更简单且无需防止抢先争用条件。结合 Task.Run() 使用时,异步编程比 BackgroundWorker 更适用于 CPU 绑定的操作,因为异步编程将运行代码的协调细节与 Task.Run 传输至线程池的工作区分开来。

六、async 和 await 修饰符

  当你使用 async 修饰符指定该方法为异步方法时:
  • 可以使用 await 来指定悬挂点。await 运算符会告诉编译器,异步方法只有直到等待的异步过程执行完成,才能继续通过该点往下执行。同时,控制权将返回至异步方法的调用方。await 表达式中异步方法在挂起后,如果该方法还没有执行完成并退出,finally 块中的将不会执行。

  • 标记的异步方法本身可以通过调用它的方法进行等待。异步方法中通常包含一个或多个 await 运算符,当然,一个 await 表达式都不存在也不会导致编译器错误,但是编译器会发出警告,该方法在执行的时候依然会依照同步方法来执行,async 其实只是一个标识的作用而已,告诉编译器他“应该”是一个异步方法。

七、返回类型和参数信息

  在编写异步方法时,我们绝大部分会使用 Task 和 Task<TResult> 作为返回类型。

  示例:

         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 关键字,但是异步方法可以调用包含这些参数的方法。

八、命名的约定

  根据约定,使用 async 的方法都应该以“Async”作为后缀,如:DownloadAsync() 。但是,如果某一约定中的事件、基类或接口有其他的形式约定,则可以忽略上述约定。例如,不应该修改或重命名常用事件处理程序,如 btnOpen_Click。

传送门

  1. 走进异步编程的世界 - 开始接触 async/await(推荐)

  2. 走进异步编程的世界 - 剖析异步方法(上)

  3. 走进异步编程的世界 - 剖析异步方法(下)

  4. 走进异步编程的世界 - 在 GUI 中执行异步操作


【参考引用】微软官方文档图片

【参考】https://msdn.microsoft.com/zh-cn/library/windows/apps/hh191443(v=vs.110).aspx

 

[.NET] 利用 async & await 的异步编程的更多相关文章

  1. 利用 async & await 的异步编程

    走进异步编程的世界 - 开始接触 async/await 利用 async & await 的异步编程 async 的三大返回类型 公司技术需求备忘录

  2. [.NET] 利用 async & await 进行异步 IO 操作

    利用 async & await 进行异步 IO 操作 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6082673.html  序 上次,博主 ...

  3. Atitit. Async await 优缺点 异步编程的原理and实现 java c# php

    Atitit. Async await 优缺点 异步编程的原理and实现 java c# php 1. async & await的来源1 2. 异步编程history1 2.1. 线程池 2 ...

  4. 用 Python 3 的 async / await 做异步编程

    前年我曾写过一篇<初探 Python 3 的异步 IO 编程>,当时只是初步接触了一下 yield from 语法和 asyncio 标准库.前些日子我在 V2EX 看到一篇<为什么 ...

  5. 使用 async/ await 进行 异步 编程

    一.异步函数 异步函数概念. 通常 是 指用 async 修饰 符 声明 的, 可 包含 await 表达式 的 方法 或 匿名 函数 1. 从 语言 的 视角 来看, 这些 await 表达式 正是 ...

  6. 使用 Async 和 Await 的异步编程(C# 和 Visual Basic)[msdn.microsoft.com]

    看到Microsoft官方一篇关于异步编程的文章,感觉挺好,不敢独享,分享给大家. 原文地址:https://msdn.microsoft.com/zh-cn/library/hh191443.asp ...

  7. 使用Async和Await进行异步编程(C#版 适用于VS2015)

    你可以使用异步编程来避免你的应用程序的性能瓶颈并且加强总体的响应.然而,用传统的技术来写异步应用是复杂的,同时编写,调试和维护都很困难. VS2012介绍了简单的方法,那就是异步编程,它在.Net F ...

  8. 使用Async和Await进行异步编程(C#版 适用于VS2015) z

    你可以使用异步编程来避免你的应用程序的性能瓶颈并且加强总体的响应.然而,用传统的技术来写异步应用是复杂的,同时编写,调试和维护都很困难. VS2012介绍了简单的方法,那就是异步编程,它在.Net F ...

  9. Async和Await进行异步编程

    使用Async和Await进行异步编程(C#版 适用于VS2015) 你可以使用异步编程来避免你的应用程序的性能瓶颈并且加强总体的响应.然而,用传统的技术来写异步应用是复杂的,同时编写,调试和维护都很 ...

随机推荐

  1. 【XSS】延长 XSS 生命期

    XSS 的本质仍是一段脚本.和其他文档元素一样,页面关了一切都销毁.除非能将脚本蔓延到页面以外的地方,那样才能获得更长的生命力. 庆幸的是,从 DOM 诞生的那一天起,就已为我们准备了这个特殊的功能, ...

  2. Android数据加密之Base64编码算法

    前言: 前面学习总结了平时开发中遇见的各种数据加密方式,最终都会对加密后的二进制数据进行Base64编码,起到一种二次加密的效果,其实呢Base64从严格意义上来说的话不是一种加密算法,而是一种编码算 ...

  3. windows+nginx+iis+redis+Task.MainForm构建分布式架构 之 (nginx+iis构建服务集群)

    本次要分享的是利用windows+nginx+iis+redis+Task.MainForm组建分布式架构,由标题就能看出此内容不是一篇分享文章能说完的,所以我打算分几篇分享文章来讲解,一步一步实现分 ...

  4. 以项目谈WebGIS中Web制图的设计和实现

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.背景介绍 一般WebGIS项目中,前端展示数据的流程基本是先做数据入 ...

  5. Git小技巧 - 指令别名及使用Beyond Compare作为差异比较工具

    前言 本文主要写给使用命令行来操作Git的用户,用于提高Git使用的效率.至于使用命令还是GUI(Tortoise Git或VS的Git插件)就不在此讨论了,大家根据自己的的喜好选择就好.我个人是比较 ...

  6. Mybatis XML配置

    Mybatis常用带有禁用缓存的XML配置 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE ...

  7. JDK动态代理

    一.基本概念 1.什么是代理? 在阐述JDK动态代理之前,我们很有必要先来弄明白代理的概念.代理这个词本身并不是计算机专用术语,它是生活中一个常用的概念.这里引用维基百科上的一句话对代理进行定义: A ...

  8. Flexible 弹性盒子模型之CSS flex-flow

    实例 让弹性盒的元素以相反的顺序显示,且在必要的时候进行拆行: display:flex; flex-flow:row-reverse wrap;   效果预览 浏览器支持 表格中的数字表示支持该属性 ...

  9. win10系统下连接无线网络掉线问题解决办法

    打开驱动精灵----系统诊断 找一个可修复的驱动点击 选择连不上网中的查看更多 有连不上网络,网络连接受限,解决无线间歇性掉网问题 进入计算机管理----设备管理 修改无线网络属性(名称含有wirel ...

  10. 在树莓派Raspbian下安装支持Hard Float的.NET环境

    [题外话] 最近入了个树莓派玩,系统装的官方推荐的Hard Float的Raspbian,由于衍生自Debian,所以Mono什么的非常好装.但是官方源中的Mono在Hard Float的Raspbi ...