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. Comparison and difference for Default geodatabase,Current workspace,Scratch workspace,Home Folder

    Comparison and difference for Default geodatabase,Current workspace,Scratch workspace,Home Folder 商务 ...

  2. Java static静态关键字 有啥用

    #static有啥用 在Java语言中,static表示“静态”的意思,使用场景可以用来修饰成员变量和成员方法,当然也可以是静态代码块.static的主要作用在于创建独立于具体对象的域变量或者方法. ...

  3. MySQL 行转列 -》动态行转列 -》动态行转列带计算

    Pivot Table Using MySQL - A Complete Guide | WebDevZoomhttp://webdevzoom.com/pivot-table-using-mysql ...

  4. leetcode 11. Container With Most Water 、42. Trapping Rain Water 、238. Product of Array Except Self 、407. Trapping Rain Water II

    11. Container With Most Water https://www.cnblogs.com/grandyang/p/4455109.html 用双指针向中间滑动,较小的高度就作为当前情 ...

  5. istio 简介

    最近接触到了 istio,感觉十分强大,写篇短文推荐给大家.本文所涉及的具体实验步骤可以参考官网教程. istio 相关文章列表: istio 简介 istio 性能测试 istio 是什么 Isti ...

  6. linux6 x86-64 RPM包安装mysql5.7.20

    注意版本和此次更新时间 2017-12-03  版本:mysql-5.7.20-1.el6.x86_64  环境:linux6.x ​官方下载地址: wget https://dev.mysql.co ...

  7. 【Mybatis】MyBatis之配置多数据源(十)

    在做项目的过程中,有时候一个数据源是不够,那么就需要配置多个数据源.本例介绍mybatis多数据源配置 前言 一般项目单数据源,使用流程如下: 单个数据源绑定给sessionFactory,再在Dao ...

  8. 使用Varnish为网站加速

    (1).Varnish概述 Varnish是一款高性能的开源HTTP加速器,可以有效降低web服务器的负载,提升访问速度.根据官方的说法,Varnish是一个cache型的HTTP反向代理. Varn ...

  9. 报错:WARN [WorkerSender[myid=1]:QuorumCnxManager@584] - Cannot open channel to 2 at election address /x.x.x.x:3888

    报错背景: zookeeper安装完成之后,启动之后正常,但是查看log文件zookeeper.log时发现报错. 报错现象: -- ::, [myid:] - INFO [WorkerSender[ ...

  10. Python - Django - ORM 分组查询补充

    单表查询: models.py: from django.db import models class Employee(models.Model): name = models.CharField( ...