C# 中 async 和 await 的基本使用
C# 中 async 和 await 的基本使用
前言
经常在 C# 的代码中看到以 Async 结尾的方法,大概知道意为异步方法,但不知道怎么使用,也不知道如何定义。
对于“同步”、“异步”、“阻塞”、"非阻塞"这几个概念还是比较清楚的。同步是指等待方法的执行完成;异步是指设置方法执行后继续其它操作,通过回调的方式对结果进行其它操作;阻塞是指执行到这一步就不往后了,直到执行完成;非阻塞是指执行这一步时,还可以进行其它操作。
这两组概念其实是讲的一个东西,只是针对的方向有些许区别(一个强调是否立即返回,一个强调是否继续往后)
对于 C# 中的 async 和 await,可以这么简单理解:async 告诉 runtime,这个函数可以异步去执行以提高效率。await 则告诉 runtime,真正耗时的是在我这个关键字后面的操作。
本文仅希望在使用的层面验证,对于原理以及是否新开线程等,由于能力有限,暂不深入
思路与实验
对于本地环境而言,读取大文件是比较耗时的操作之一。因此先写一个读取文件的操作,再用 async 和 await 的方法将其包裹,以探究这两个关键字的使用(为了模拟执行一番后得到最后的结果,我们返回二进制文件的最后一个字节所代表的数字)。
1. 初步代码,同步调用耗时方法
using System;
using System.IO;
namespace AsyncAwaitTest
{
class Program
{
static void Main(string[] args)
{
DateTime time = DateTime.Now;
byte targetNum;
Console.WriteLine("模拟执行其它操作,用 A 表示");
targetNum = ReadLargeFile(); // 为体现同步异步区别,执行三遍
targetNum = ReadLargeFile();
targetNum = ReadLargeFile();
Console.WriteLine("最后一个字节所代表的数字为:" + targetNum);
Console.WriteLine("模拟执行其它操作,用 B 表示");
Console.WriteLine("耗时为:" + (DateTime.Now - time).Seconds);
Console.ReadLine();
}
/// <summary>
/// 读取大文件(耗时方法)
/// </summary>
/// <returns></returns>
private static byte ReadLargeFile()
{
const int BUFFER_SIZE = 4096;
FileStream fileStream = new FileStream(
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Windows10.iso"),
FileMode.Open, FileAccess.Read, FileShare.Read); // 在此处设置允许共享
byte[] buffer = new byte[BUFFER_SIZE];
int readOutCount = 0, lastReadOutCount = 0;
while ((readOutCount = fileStream.Read(buffer, 0, BUFFER_SIZE)) != 0)
{
lastReadOutCount = readOutCount;
}
return buffer[lastReadOutCount - 1];
}
}
}

可以看出,耗时约 10 s。
2. 使用异步关键字包裹同步方法
新增函数 AsyncCallReadLargeFile,并修改 main 函数中的调用。通过查阅资料可以得知,Task 类的 Result 方法在执行时会阻塞。
using System;
using System.IO;
using System.Threading.Tasks;
namespace AsyncAwaitTest
{
class Program
{
static void Main(string[] args)
{
......
Console.WriteLine("模拟执行其它操作,用 A 表示"); // 执行顺序 1
Task<byte> t1 = AsyncCallReadLargeFile(); // 为体现同步异步区别执行三遍
Task<byte> t2 = AsyncCallReadLargeFile();
Task<byte> t3 = AsyncCallReadLargeFile();
Console.WriteLine("模拟执行其它操作,用 C 表示"); // 执行顺序 3 或 4
targetNum = t1.Result;
targetNum = t2.Result;
targetNum = t3.Result;
Console.WriteLine("最后一个字节所代表的数字为:" + targetNum); // 执行顺序 6
Console.WriteLine("模拟执行其它操作,用 B 表示"); // 执行顺序 7
......
}
/// <summary>
/// 使用异步关键字包裹同步方法
/// </summary>
/// <returns></returns>
private static async Task<byte> AsyncCallReadLargeFile()
{
Console.WriteLine("模拟执行异步子方法,用 a 表示"); // 执行顺序 2
byte result = await Task.Run(ReadLargeFile);
Console.WriteLine("模拟执行异步子方法,用 b 表示"); // 执行顺序 5
return result;
}
/// <summary>
/// 读取大文件(耗时方法)
/// </summary>
/// <returns></returns>
private static byte ReadLargeFile()
{
Console.WriteLine("读取文件"); // 执行顺序 3 或 4
......
}
}
}

小结:通过耗时可以明显看出:
(1)我们的异步方法确实是以异步的方式执行了(对同一文件进行三个异步读操作,耗时没有叠加)
(2)大致的执行顺序如代码注释中所示,也即,使用 await 时,确实等待执行完成当前后才会执行异步函数中后续的方法
(3)即使在异步函数中,未用 await 修饰的方法也是同步执行的(通过截图无法看出,但通过观察代码输出可以看出)
其它一些思考
1. 异步的方法最终会由同步方法调用
这句话看上去有点绝对了,但确实是这个道理。从写法上:写函数时,有 async 就必须有 await(否则会警告,并且以同步方式执行),有 await 就必须有 async(否则会报错),而异步函数必须要使用这两个成对出现的关键字。从道理上:异步方法就是来解决同步方法顺序执行过于循规蹈矩问题的,没有同步方法的调用怎么会有这些问题呢?
2. async,await 和 Task 什么关系
尝试过这一种写法:
/* 错误写法 */
private static async byte AsyncCallReadLargeFile()
{
return await AsyncCallReadLargeFile();
}
会有如下错误提示:
错误 CS1061 “byte”未包含“GetAwaiter”的定义,并且找不到可接受第一个“byte”类型参数的可访问扩展方法“GetAwaiter”(是否缺少 using 指令或程序集引用?)
似乎可以认为,只有返回的类型包含 GetAwaiter 的定义,才能被当作异步函数来调用。最常见的只有 Task 包含这个方法。想到之前看到过,async 修饰的函数,返回类型只能是 void, Task, Task。
3. 异步方法的返回
在 AsyncCallReadLargeFile 函数中,虽然签名中返回类型是 Task<byte> ,但我们实际上只返回了 byte 类型,并没有 Task。我的理解是对于 async 修饰的异步方法,返回的类型会自动被包装成 Task 的泛型类型。
参考
深入理解async和await的作用及各种适用场景和用法(旧,详见最新两篇)
(这两篇都很全面,受益匪浅)
C# 中 async 和 await 的基本使用的更多相关文章
- JavaScript中async和await的使用以及队列问题
宏任务和微任务的队列入门知识,可以参考之前的文章: JavaScript的事件循环机制 宏任务和微任务在前端面试中,被经常提及到,包括口头和笔试题 async && await概念 a ...
- C#中async和await用法
.net 4.5中新增了async和await这一对用于异步编程的关键字. async放在方法中存在await代码的方法中,await放在调用返回Task的方法前. class Class1 { pr ...
- ES6中async与await的使用方法
promise的使用方法 promise简介 是异步编程的一种解决方案.从语法上说,Promise 是一个对象,从它可以获取异步操作的消息.解决回调函数嵌套过多的情况 const promise =n ...
- ES6中async和await说明和用法
昨天看了一篇vue的教程,作者用async/ await来发送异步请求,从服务端获取数据,代码很简洁,同时async/await 已经被标准化,是时候学习一下了. 先说一下async的用法,它作为一个 ...
- C# 中的Async 和 Await 的用法详解
众所周知C#提供Async和Await关键字来实现异步编程.在本文中,我们将共同探讨并介绍什么是Async 和 Await,以及如何在C#中使用Async 和 Await. 同样本文的内容也大多是翻译 ...
- 【转】【C#】C# 5.0 新特性——Async和Await使异步编程更简单
一.引言 在之前的C#基础知识系列文章中只介绍了从C#1.0到C#4.0中主要的特性,然而.NET 4.5 的推出,对于C#又有了新特性的增加--就是C#5.0中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#基础知 ...
- 四、C# 5.0 新特性——Async和Await使异步编程更简单
一.引言 .NET 4.5 的推出,对于C#又有了新特性的增加--就是C#5.0中async和await两个关键字,这两个关键字简化了异步编程,之所以简化了,还是因为编译器给我们做了更多的工作,下面就 ...
随机推荐
- MySQL Utilities工具教程
一.MySQL Utilities介绍 MySQL Utilities 提供一组命令行工具用于维护和管理 MySQL 服务器,包括: 管理工具 (克隆.复制.比较.差异.导出.导入)复制工具 (安装. ...
- 跟我一起写 Makefile(十一)
make 的运行 ------ 一般来说,最简单的就是直接在命令行下输入make命令,make命令会找当前目录的makefile来执行,一切都是自动的.但也有时你也许只想让make重编译某些文件,而不 ...
- WPF 线程开启等待动画
public static Dictionary<string, object> Dic = new Dictionary<string, object>();//定义一个字典 ...
- 带你梳理Jetty自定义ProxyServlet实现反向代理服务
摘要:最近要做一个将K8s中的某组件UI通过反向代理映射到自定义规则的链接地址上,提供给用户访问的需求.所以顺便研究了一下Jetty的ProxyServlet. 本文分享自华为云社区<Jetty ...
- docker运行gerrit(代码审查工具)
gerrit是什么? Gerrit,一种免费.开放源代码的代码审查软件,使用网页界面. gerrit背景 Gerrit,一种免费.开放源代码的代码审查软件,使用网页界面.利用网页浏览器,同一个团队的软 ...
- servlet防止表单重复提交
日常开发中,防表单重复提交是一项必须的工作 我们可以利用javascript防止表单重复提交,但是利用javascript防止表单重复提交会出现一个新的问题 因为某些用户可能会绕过script代码直接 ...
- 服务端负载监控-参考srs服务器源码
#include <map> #include <stdio.h> using namespace std; struct SrsMemoryObject { void* pt ...
- jdbc获取PreparedStatement最终执行的sql语句
//直接打印PreparedStatement对象 System.out.println(ps); 输出结果: com.mysql.jdbc.JDBC42PreparedStatement@5f205 ...
- react启动报babel-eslint依赖版本不一致
遇到的问题: gitlab上拉的react的项目,下载依赖之后,yarn start启动报错 因为按他说的步骤:1.改动比较大, 2.完全没什么作用(重点) 解决方案:直接忽略,如果项目没有.env文 ...
- SpringBoot的快速入门
快速创建一个SpringBoot项目(两种方式:STS版本,IntelliJ IDEA) 1.STS方式:什么是STS?是Spring团队推荐使用的开发工具 所谓的sts就是eclipse升级版 继承 ...