C# async 和 await 理解

先假设如下场景:

主函数 Main,循环等待用户输入;

计算函数 Cal,耗时计算大量数据;

class Test
{
static int Main(string[] args)
{
while(true)
{
// 等待用户输入
}
} public static int Cal() {
int sum = 0;
for (int i = 0; i < 999; i++)
{
sum = sum + i;
}
Console.WriteLine($"sum={sum}");
return sum;
}
}

为了在Main函数中调用Cal函数,同时Cal函数不阻塞主函数的循环,此时需要考虑增加一个CalAsync函数使Cal函数异步执行。

传统的思维方法  在CalAsync函数中启动一个线程,并在线程中执行Cal函数:

// using System.Threading;
public static CalAsync()
{
Thread td = new Thread(new ThreadStart(Cal));
td.start();
}

这种方法显示地创建了一个线程并启动执行,CalAsync函数本身还是在主线程执行并且无法直接获取Cal函数的结果。

async 和 await 异步编程方法  使用async标记CalAsync函数,并在CalAsync函数中创建任务Task异步执行Cal函数,同时使用await标记获取Cal函数的执行结果:

// using System.Threading.Tasks;
public static aysnc void CalAsync()
{
int result = await Task.Run(new Func<int>(Cal));
// 或使用lambda书写方式
// int result = await Task.Run(() => test());
Console.WriteLine(result);
}

在Main函数中直接调用CalAsync函数,可以发现CalAsync成功调用了Cal函数并在一段时间后输出了结果,同时Main函数并不会被阻塞。

分别在Main、Cal、CalAsync函数中增加代码打印当前线程ID:

class Test
{
static void Main(string[] args)
{
string tid = Thread.CurrentThread.ManagedThreadId.ToString();
Console.WriteLine($"Main1 tid {tid}");
Task<int> t = CalAsync();
Console.WriteLine($"Main after CalAsync");
Console.Read();
} public static int Cal()
{
string tid = Thread.CurrentThread.ManagedThreadId.ToString();
Console.WriteLine($"Cal tid {tid}");
int sum = 0;
for (int i = 0; i < 999; i++)
{
sum = sum + i;
}
Console.WriteLine($"sum={sum}");
return sum;
} public static async Task<int> CalAsync()
{
string tid = Thread.CurrentThread.ManagedThreadId.ToString();
Console.WriteLine($"CalAsync1 tid {tid}");
int result = await Task.Run(new Func<int>(Cal));
tid = Thread.CurrentThread.ManagedThreadId.ToString();
Console.WriteLine($"CalAsync2 tid {tid}, result={result}");
return result;
}
}

结果如图:

可以看出,在CalAsync函数中,await标记之前,代码在主线程中执行,而await标记之后,代码在子线程中执行。

理解与结论:

  • 在C#中, async标记了一个包含异步执行的函数,通过async标记的函数若在主线程中直接调用,则函数一开始仍在主线程中执行;

  • aysnc标记的函数内部必须包含await标记需要异步执行的函数(根据vs2017编译提示),若当前函数在主线程中直接调用,则await标记前的代码在主线程中执行,await标记后的代码在其异步子线程中执行;

  • async标记的函数返回值必须为void、Task、Task< TResult> 类型,可以理解为async标记的函数返回的是 “空”、“即将执行的任务”、“带结果的即将执行的任务”实例;

  • async标记的函数可以继续往下调用async标记函数,调用形式如下例, 从调用逻辑可以理解为await实际上用来触发所标记的Task任务异步执行,并最后获取异步执行的返回值,从运行过程看该触发应该仅对最终的Task任务有效:

 public static async Task<int> CallCalAsync()
{
string tid = Thread.CurrentThread.ManagedThreadId.ToString();
Console.WriteLine($"CallCalAsync1 tid {tid}");
int result = await CalAsync();
tid = Thread.CurrentThread.ManagedThreadId.ToString();
Console.WriteLine($"CallCalAsync2 tid {tid}, result={result}");
return result;
}

总结

C#中async与await异步编程,可以理解为:

1. async声明了一个包含异步执行代码的函数,该函数执行时不会阻塞调用线程;

2. await存在于async函数中,声明了一个异步执行入口,程序动态运行时从该入口创建并进入一个异步线程环境,并在该线程执行任务实例及任务实例返回之后的代码;

3. 一个async函数中声明多个await关键字时,程序将代码顺序创建并进入异步子线程执行任务实例及任务实例返回之后的代码直到下一个await声明处, 最后一个await声明之后的代码会在最后一个异步子线程中执行 ;

3. await标记的右侧代码返回或定义了一个任务实例,该实例由需要异步执行的目标耗时函数初始化,并在最终定义处触发异步执行。

C# async 和 await 理解的更多相关文章

  1. async和await理解代码

    <1>:Async和Await的理解1 using System; using System.Collections.Generic; using System.Linq; using S ...

  2. await和async更多的理解

    最近有不少网友提起await和async,呵呵,C# 5引进的语法糖. 这个语法糖还真不好吃,能绕倒一堆初学的朋友,在网上也有很多网友关于这块知识点的争论,有对有错,今天在这里把这个误区好好讲讲. 在 ...

  3. ES7前端异步玩法:async/await理解

    在最新的ES7(ES2017)中提出的前端异步特性:async.await. 什么是async.await? async顾名思义是"异步"的意思,async用于声明一个函数是异步的 ...

  4. 第十五节:深入理解async和await的作用及各种适用场景和用法

    一. 同步VS异步 1.   同步 VS 异步 VS 多线程 同步方法:调用时需要等待返回结果,才可以继续往下执行业务 异步方法:调用时无须等待返回结果,可以继续往下执行业务 开启新线程:在主线程之外 ...

  5. .net 中的async,await理解

    理解: 1.async修饰的方法可理解为异步方法(必须要配合await,否则和普通方法无异)2.当async方法执行遇到await,则立即将控制权转移到async方法的调用者3.由调用者决定是否需要等 ...

  6. Ext JS学习第十六天 事件机制event(一) DotNet进阶系列(持续更新) 第一节:.Net版基于WebSocket的聊天室样例 第十五节:深入理解async和await的作用及各种适用场景和用法 第十五节:深入理解async和await的作用及各种适用场景和用法 前端自动化准备和详细配置(NVM、NPM/CNPM、NodeJs、NRM、WebPack、Gulp/Grunt、G

    code&monkey   Ext JS学习第十六天 事件机制event(一) 此文用来记录学习笔记: 休息了好几天,从今天开始继续保持更新,鞭策自己学习 今天我们来说一说什么是事件,对于事件 ...

  7. ES7前端异步玩法:async/await理解 js原生API妙用(一)

    ES7前端异步玩法:async/await理解   在最新的ES7(ES2017)中提出的前端异步特性:async.await. 什么是async.await? async顾名思义是“异步”的意思,a ...

  8. C# 多线程(18):一篇文章就理解async和await

    目录 前言 async await 从以往知识推导 创建异步任务 创建异步任务并返回Task 异步改同步 说说 await Task 说说 async Task 同步异步? Task封装异步任务 关于 ...

  9. 探索c#之Async、Await剖析

    阅读目录: 基本介绍 基本原理剖析 内部实现剖析 重点注意的地方 总结 基本介绍 Async.Await是net4.x新增的异步编程方式,其目的是为了简化异步程序编写,和之前APM方式简单对比如下. ...

随机推荐

  1. 快速克隆网站利器-teleport ultra

    快速克隆网站利器-teleport ultra 一.下载 二.操作步骤 第一步:打开这个软件 第二步点击file.下拉选择 New Project Wizred,弹出如下下拉框 这里我们使用第一个或者 ...

  2. vim 显示行号 临时&永久

    设置vim 永久显示行号 - electrocrazy的博客 - CSDN博客https://blog.csdn.net/electrocrazy/article/details/79035216 v ...

  3. 小D课堂 - 零基础入门SpringBoot2.X到实战_第10节 SpringBoot整合定时任务和异步任务处理_41、SpringBoot定时任务schedule讲解

    笔记 1.SpringBoot定时任务schedule讲解     简介:讲解什么是定时任务和常见定时任务区别 1.常见定时任务 Java自带的java.util.Timer类            ...

  4. leetcode 542. 01 Matrix 、663. Walls and Gates(lintcode) 、773. Sliding Puzzle 、803. Shortest Distance from All Buildings

    542. 01 Matrix https://www.cnblogs.com/grandyang/p/6602288.html 将所有的1置为INT_MAX,然后用所有的0去更新原本位置为1的值. 最 ...

  5. C2678 二进制“>>”: 没有找到接受“std::stringstream”类型的左操作数的运算符(或没有可接受的转换)

    C2678 二进制“>>”: 没有找到接受“std::stringstream”类型的左操作数的运算符(或没有可接受的转换)

  6. 深入学习c++--智能指针(三) unique_ptr

    1. 几种智能指针 1. auto_ptr: c++11中推荐不使用他(放弃) 2. shared_ptr: 拥有共享对象所有权语义的智能指针 3. unique_ptr: 拥有独有对象所有权语义的智 ...

  7. 算法习题---4-7RAID技术(UV509)

    一:题目 (一)基础知识补充(RAID和奇偶校验) 磁盘管理—磁盘阵列(RAID)实例详解(本题目常用RAID 5技术实现) 奇偶校验(同行数据中同位上的1的个数,偶校验时:1的个数为偶数则校验结果为 ...

  8. Promise.resolve的作用

    Promise.resolve方法有下面三种形式: Promise.resolve(value); Promise.resolve(promise); Promise.resolve(thenable ...

  9. Spring Cloud与Docker微服务架构实战 PDF版 内含目录

    Spring Cloud与Docker微服务架构实战  目录 1 微服务架构概述 1 1.1 单体应用架构存在的问题1 1.2 如何解决单体应用架构存在的问题3 1.3 什么是微服务3 1.4 微服务 ...

  10. 报错:ModuleNotFoundError: No module named '_ctypes'

    报错背景: CentOS 7 Python3.7 安装 setuptools 插件的时候报错. 报错现象: [root@master setuptools-]# python3. setup.py b ...