《C#并发编程经典实例》学习笔记—异步编程关键字 Async和Await
C# 5.0 推出async和await,最早是.NET Framework 4.5引入,可以在Visual Studio 2012使用。在此之前的异步编程实现难度较高,async使异步编程的实现变得简便。
各平台对async的支持情况
| 平台 | async |
|---|---|
| .NET 4.5及以上 | ✓ |
| .NET 4.0 | NuGet |
| Mono iOS/Droid | ✓ |
| Windows Store | ✓ |
| Windows Phone Apps 8.1 | ✓ |
| Windows Phone SL 8.0 | ✓ |
| Windows Phone SL 7.1 | NuGet |
| Silverlight 5 | NuGet |
在不支持的平台,安装NuGet包 Microsoft.Bcl.Async
使用
async修饰符可将方法、lambda 表达式或匿名方法指定为异步。
async 对方法做了什么处理
从使用async修饰符修饰的方法的IL代码可以得出一个结论:
- 在Debug下,针对async方法,生成的是一个class状态机
- 在Release下,针对async方法,生成的是一个struct状态机
举例:
C#代码如下
using System.Threading.Tasks;
namespace ConsoleApp3
{
public class Test
{
public async Task TestAsync()
{
await GetAsync();
}
public async Task GetAsync()
{
await Task.Delay(1);
}
}
}
以TestAsync方法为准
Release下 初始化状态机V_0 ,类型是值类型Struct(valuetype),类型名称为<TestAsync>d__0
.locals init (
[0] valuetype ConsoleApp3.Test/'<TestAsync>d__0' V_0,
[1] valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder V_1
)
<TestAsync>d__0 继承值类型[mscorlib]System.ValueType
.class nested private sealed auto ansi beforefieldinit
'<TestAsync>d__0'
extends [mscorlib]System.ValueType
implements [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine
Debug 下 初始化状态机V_0 ,类型是引用类型Class(class) ,类型名称为<TestAsync>d__0
.locals init (
[0] class ConsoleApp3.Test/'<TestAsync>d__0' V_0,
[1] valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder V_1
)
<TestAsync>d__0 继承引用类型[mscorlib]System.Object
.class nested private sealed auto ansi beforefieldinit
'<TestAsync>d__0'
extends [mscorlib]System.Object
implements [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine
异步方法的定义和注意事项
使用
async关键字定义的异步方法简称为“异步方法”。
注意事项:
- 如果
async关键字修改的方法不包含await表达式或语句,则该方法将同步执行。 编译器警告将通知你不包含await语句的任何异步方法,因为该情况可能表示存在错误。 请参阅编译器警告(等级 1)CS4014。 async关键字是上下文关键字,原因在于只有当它修饰方法、lambda 表达式或匿名方法时,它才是关键字。 在所有其他上下文中,都会将其解释为标识符。- 不要用
void作为async方法的返回类型!async方法可以返回void,但是这仅限于编写事件处理程序。一个普通的async方法如果没有返回值,要返回Task,而不是void。 - 一定要避免使用
Task.Wait或Task<T>.Result方法,因为它们会导致死锁。如果使用了async,最好就一直使用它。 - 异步方法的参数不能使用
out,ref。out或ref返回的数据应借用Task<TResult>返回,可以使用元组或自定义数据结构。
异步方法的特征
- 方法签名包含
async修饰符。 - 按照约定,异步方法的名称以“Async”后缀结尾。
- 返回类型为下列类型之一:
- 如果你的方法有操作数为
TResult类型的返回语句,则为Task<TResult>。 - 如果你的方法没有返回语句或具有没有操作数的返回语句,则为
Task。 void:如果要编写异步事件处理程序。- 包含
GetAwaiter方法的其他任何类型(自 C# 7.0 起)。
- 如果你的方法有操作数为
- 方法通常包含至少一个 await 表达式,该表达式标记一个点,在该点上,直到等待的异步操作完成方法才能继续。 同时,将方法挂起,并且控制返回到方法的调用方。
关于async和await具体的执行流程,方法何时挂起和释放,请参考异步程序中的控制流 (C#)
异步返回类型
上面提到 void 作为返回结果,适用于事件处理程序。
举例:
using System;
using System.Threading.Tasks;
namespace ConsoleApp3
{
public class TestVoidAsync
{
private event EventHandler<EventArgs> DoTest;
public TestVoidAsync()
{
DoTest += DoTestEvent;
}
private static async void DoTestEvent(object sender, EventArgs e)
{
await Task.Delay(1000);
}
protected virtual void OnDoTest()
{
DoTest?.Invoke(this, EventArgs.Empty);
}
}
}
void 作为返回结果存在一个弊端:无法捕获异常。
返回 void 的异步方法的调用方无法捕获从该方法引发的异常,且此类未经处理的异常可能会导致应用程序故障。 如果返回
Task或Task<TResult>的异步方法中出现异常,此异常将存储于返回的任务中,并在等待该任务时再次引发。
通用的异步返回类型:
从 C# 7.0 开始,异步方法可返回任何具有可访问的 GetAwaiter 方法的类型。
ValueTask<TResult>:
Task 和 Task<TResult> 是引用类型,因此,性能关键路径中的内存分配会对性能产生负面影响,尤其当分配出现在紧凑循环中时。 支持通用返回类型意味着可返回轻量值类型(而不是引用类型),从而避免额外的内存分配。
使用ValueTask<TResult>,需要添加NuGet包 System.Threading.Tasks.Extensions。
ValueTask<TResult> 是struct值类型,Task 和 Task<TResult> 是class引用类型
异步操作的生命周期
Task 类提供了异步操作的生命周期,且该周期由 TaskStatus 枚举表示。
| 状态 | 执行顺序 | 备注 |
|---|---|---|
| Created | 0 | 该任务已初始化,但尚未安排。 |
| WaitingForActivation | 1 | 该任务正在等待被.NET Framework infrastructure 内部激活和调度。 |
| WaitingToRun | 2 | 该任务已安排执行但尚未开始执行。 |
| Running | 3 | 任务正在运行但尚未完成。 |
| WaitingForChildrenToComplete | 4 | 任务已完成执行,并隐式等待附加的子任务完成。 |
| RanToCompletion | 5 | 任务已成功完成执行。 |
| Canceled | 6 | 引发 OperationCanceledException 异常,或者在任务开始执行之前取消 |
| Faulted | 7 | 由于未处理的异常,任务已完成。 |
Canceled 和 Faulted状态都会因为任务异常导致转换为该状态。二者的区别如下:
如果标记的 IsCancellationRequested 属性返回 false,或者异常的标记与任务的标记不匹配,则会将 OperationCanceledException 按照普通的异常来处理,从而导致任务转换为 Faulted 状态。 另外还要注意,其他异常的存在将也会导致任务转换为 Faulted 状态。 您可以在 Status 属性中获取已完成任务的状态。
参考文章:
《C#并发编程经典实例》学习笔记—异步编程关键字 Async和Await的更多相关文章
- 重新想象 Windows 8 Store Apps (44) - 多线程之异步编程: 经典和最新的异步编程模型, IAsyncInfo 与 Task 相互转换
[源码下载] 重新想象 Windows 8 Store Apps (44) - 多线程之异步编程: 经典和最新的异步编程模型, IAsyncInfo 与 Task 相互转换 作者:webabcd 介绍 ...
- 《C#并发编程经典实例》笔记
1.前言 2.开宗明义 3.开发原则和要点 (1)并发编程概述 (2)异步编程基础 (3)并行开发的基础 (4)测试技巧 (5)集合 (6)函数式OOP (7)同步 1.前言 最近趁着项目的一段平稳期 ...
- nodejs学习笔记 —— 异步编程解决方案
在js或者node编程中,由于异步的频繁和广度使用,使得回调和嵌套的深度导致编程的体验遇到一些挑战,如果写出优雅和好看的代码,本文主要针对异步编程的主流方案做一些总结 1.事件发布/订阅模式 事件监听 ...
- 09-Node.js学习笔记-异步编程
同步API,异步API 同步API:只有当前API执行完成后,才能继续执行下一个API console.log('before'); console.log('after'); 异步API:当前API ...
- 多线程之异步编程: 经典和最新的异步编程模型,async与await
经典的异步编程模型(IAsyncResult) 最新的异步编程模型(async 和 await) 将 IAsyncInfo 转换成 Task 将 Task 转换成 IAsyncInfo 示例1.使用经 ...
- 多线程之异步编程: 经典和最新的异步编程模型, IAsyncInfo 与 Task 相互转换
经典的异步编程模型(IAsyncResult) 最新的异步编程模型(async 和 await) 将 IAsyncInfo 转换成 Task 将 Task 转换成 IAsyncInfo 示例1.使用经 ...
- 《C#并发编程经典实例》学习笔记—2.7 避免上下文延续
避免上下文延续 在默认情况下,一个 async 方法在被 await 调用后恢复运行时,会在原来的上下文中运行. 为了避免在上下文中恢复运行,可让 await 调用 ConfigureAwait 方法 ...
- 《C#并发编程经典实例》学习笔记—3.1 数据的并行处理
问题 有一批数据,需要对每个元素进行相同的操作.该操作是计算密集型的,需要耗费一定的时间. 解决方案 常见的操作可以粗略分为 计算密集型操作 和 IO密集型操作.计算密集型操作主要是依赖于CPU计算, ...
- 《C#并发编程经典实例》学习笔记—2.3 报告任务
问题 异步操作时,需要展示该操作的进度 解决方案 IProgress<T> Interface和Progress<T> Class 插一段话:读<C#并发编程经典实例&g ...
随机推荐
- 用pyspider爬取并解析json字符串
获取堆糖网站所有用户的id 昵称及主页地址 #!/usr/bin/env python # -*- encoding: utf-8 -*- # Created on 2016-06-21 13:57: ...
- 在 npm 中使用 ES6 module
node 从 v8.5.0起 支持了 ES6 module. 只需保存文件名为 .mjs ,并通过一个option 可以开启执行,如 node --experimental-modules index ...
- pycharm的console显示乱码和中文的配置
第一种方式: 在python脚本开始的地方加入指定编码方式 # -*- coding : UTF-8 -*- 第二种方式: 有些时候我们会得到这种格式的字符串: "name": & ...
- linux网络编程-CRC校验
1.CRC校验是什么?为什么要使用CRC校验? CRC 即 循环冗余校验 是一种差错检测方法,可以做到对帧的无差错接受 因为现实的通信链路都不会是理想的,比特在传输的过程中有可能出现差错, 为了保证 ...
- FFmpeg命令行工具学习(二):播放媒体文件的工具ffplay
一.简述 ffplay是以FFmpeg框架为基础,外加渲染音视频的库libSDL构建的媒体文件播放器. 在使用ffplay之前必须要安装到系统中,MAC的安装教程为:http://www.cnblog ...
- 吴恩达机器学习笔记29-神经网络的代价函数(Cost Function of Neural Networks)
假设神经网络的训练样本有
- JavaScript标识符与关键字和保留字
区分大小写 JavaScript中的一切(变量.函数名.操作符)都区分大小写.例如,变量名itbsl和变量名ITbsl是两个不同的变量. 标识符 所谓标识符,就是指变量.函数.属性的名字,或者函数的参 ...
- Java面试集合(六)
1. abstract抽象 什么是abstract,中文为抽象,从具体事物抽出,概括它们共同的方面,本质属性与关系等,称为抽象.看不见,摸不着的东西叫做抽象,抽象是人们对世界万物的感觉,用特定的图像表 ...
- vue实现一个简易Popover组件
概述 之前写vue的时候,对于下拉框,我是通过在组件内设置标记来控制是否弹出的,但是这样有一个问题,就是点击组件外部的时候,怎么也控制不了下拉框的关闭,用户体验非常差. 当时想到的解决方法是:给根实例 ...
- MySql必备技能 不会的赶紧get一下 可以说很详细了
1.Mysql服务 mysql服务如何开启: 下载了mysql数据库你的服务中会有mysql服务. 1.1: 1.2: 2.使用sql语句进行 建库.建表.等操作. 2.1:使用sql语句进行创建数据 ...