C#异步编程看这篇就够了
随着.NET Core的流行,相信你现在的代码中或多或少的会用到async以及await吧!毕竟已成标配。那么我们为什么要用async以及await呢?其实这是微软团队为我们提供的一个语法糖,让我们不用996就可以轻松的编写异步代码,并无太过神奇的地方。那么,问题来了,什么是异步?异步到底又是怎样的一个过程呢?
从一个故事说起
在开始讲异步前我们先从一个生活中的小故事说起吧。话说2019年12月15日周日这一天有位程序猿小祝在这天居然没有加班,选择在家休息了,然后他习惯性的用Microsoft To Do罗列了一下这天要做的事情,如下图所示:

这一天这个程序猿小祝计划早上九点起床洗澡,然后吃早餐,洗衣服,分享一篇关于C#异步相关的文章,晚上在家加下班~~没错,这个苦逼休息的时候也得工作,不然下周的任务有可能完不成要挨批了。
这个时候这个程序猿小祝可以选择,1.起床洗澡,2.吃早餐,3.洗衣服,4.写文章,5.打会球然后“远程写代码”。这个过程有严格的执行顺序,这个过程可以视为一个同步的过程。如下图所示:

当然,这个程序猿小祝却采用了另一种方式来进行:起床后先把衣服换下来用洗衣机洗了,然后开始洗澡,然后吃饭,写了一会文章,然后等衣服洗好后再把衣服给晾好继续回来写文章,最后在晚上的时候远程写代码。在这个过程中这个程序猿在洗衣服的同时就去洗澡,吃饭写了会文章了,这个过程就是一个异步的过程。
可能这个故事比喻的不恰当,不过大伙将就着看下吧,总结一下同步跟异步吧:
- 同步方法:可以认为程序是按照你写这些代码时所采用的顺序执行相关的指令的。
 - 异步方法:可以在尚未完成所有指令的时候提前返回(如上面的洗衣服过程没执行完就返回去洗澡了),等到该方法等候的那项任务执行完毕后,在令这个方法从早前还没执行完的那个地方继续往下运行(如:衣服洗好晾好后,继续写文章了)。
 
下面我们结合伪代码来进行更加详细的讲解吧。
伪代码实例讲解
这一节我们就用伪代码来分别实现下同步过程及异步过程吧。
同步过程
下面我们用伪代码来实现上述故事中的过程吧。
 static void Main(string[] args)
        {
            Console.WriteLine("Main异步演示开始~~~~~");
            Stopwatch stopwatch = Stopwatch.StartNew();
            Bash();//洗澡
            BreakFast();//吃早餐
            WashClothes();//洗衣服
            WriteArticle();//写文章
            WritingCode();//写代码
            Console.WriteLine("Main异步演示结束~~~~~共用时{0}秒!", stopwatch.ElapsedMilliseconds/1000);
            Console.ReadKey();
        }
        private static void Bash()
        {
            Console.WriteLine("洗澡开始~~~~~");
            Thread.Sleep(1*1000);//模拟过程
            Console.WriteLine("洗澡结束~~~~~");
        }
        private static void BreakFast()
        {
            Console.WriteLine("吃早餐开始~~~~~");
            Thread.Sleep(1 * 1000);//模拟过程
            Console.WriteLine("吃早餐结束~~~~~");
        }
        private static void WashClothes()
        {
            Console.WriteLine("洗衣服开始~~~~~");
            Thread.Sleep(6 * 1000);//模拟过程
            Console.WriteLine("洗衣服结束~~~~~");
        }
        private static void WriteArticle()
        {
            Console.WriteLine("写文章开始~~~~~");
            Thread.Sleep(20 * 1000);//模拟过程
            Console.WriteLine("写文章结束~~~~~");
        }
        private static void WritingCode()
        {
            Console.WriteLine("写代码开始~~~~~");
            Thread.Sleep(12 * 1000);//模拟过程
            Console.WriteLine("写代码结束~~~~~");
        }
上面的代码没什么难的,写完代码后我们直接dotnet run一下代码,如下图所示:

我们可以看到这个代码的执行过程是严格按照我们编码的顺序执行的,即同步运行的代码。这里用时共40秒!
异步过程
我们只需要稍微改造下使得代码异步执行再来看下效果吧!伪代码如下:
 static async Task Main(string[] args)
        {
            Console.WriteLine("Main异步演示开始~~~~~");
            Stopwatch stopwatch = Stopwatch.StartNew();
            List<Task> tasks = new List<Task>
            {
                Bash(),//洗澡
            };
            tasks.Add(BreakFast());//吃早餐
            tasks.Add(WashClothes());//洗衣服
            tasks.Add(WriteArticle());//写文章
            tasks.Add(WritingCode());//写代码
            await Task.WhenAll(tasks);
            Console.WriteLine("Main异步演示结束~~~~~共用时{0}秒!", stopwatch.ElapsedMilliseconds/1000);
            Console.ReadKey();
        }
        private static async Task Bash()
        {
            Console.WriteLine("洗澡开始~~~~~");
            await Task.Delay(1*1000);//模拟过程
            Console.WriteLine("洗澡结束~~~~~");
        }
        private static async Task BreakFast()
        {
            Console.WriteLine("吃早餐开始~~~~~");
            await Task.Delay(1 * 1000);//模拟过程
            Console.WriteLine("吃早餐结束~~~~~");
        }
        private static async Task WashClothes()
        {
            Console.WriteLine("洗衣服开始~~~~~");
            await Task.Delay(6 * 1000);//模拟过程
            Console.WriteLine("洗衣服结束~~~~~");
        }
        private static async Task WriteArticle()
        {
            Console.WriteLine("写文章开始~~~~~");
            await Task.Delay(20 * 1000);//模拟过程
            Console.WriteLine("写文章结束~~~~~");
        }
        private static async Task WritingCode()
        {
            Console.WriteLine("写代码开始~~~~~");
            await Task.Delay(12 * 1000);//模拟过程
            Console.WriteLine("写代码结束~~~~~");
        }
然后我们再直接dotnet run一下代码,如下图所示:

我们可以看到这个代码的执行过程中遇到await后就会返回执行了,待await的代码执行完毕后才继续执行接下来的代码的!为了避免有的读者看不懂,我简单分析其中一个方法的执行过程吧。具体的还需要你自己把异步代码拷贝下来,多打几个断点,然后把等待时间*100(时间长点方便我们查看断点的进入顺序,否则时间短,还没来得及进断点可能代码已经执行完了)看看断点的进入步骤吧!

我也只列了一部分,具体的你们自行打断点看下吧。
异步原理解析
通过上面的伪代码分析相信你已经对异步有所了解了。接下来我们就来看看系统到底是怎么实现出这样的效果的。下面只是简单地进行下表述,如果不正确的欢迎大家指正。
编译器在处理异步方法的时候,会构建一种机制,该机制可以启动await 语句所要等候的那项异步任务,并使得程序在该工作完成之后,能够用某个线程继续执行await语句后面的那些代码。这个await语句正是关键所在。编译器会构建相应的数据结构,并把await之后的指令表示成delegate,使得程序在处理完那项异步任务之后,能够继续执行下面的那些指令。编译器会把当前方法中的每一个局部变量的值都保存在这个数据结构中,并根据await语句所要等候的任务来配置相应的逻辑,让程序能够在该任务完成之后指派某个线程,从await语句的下一条指令开始继续执行。实际上,这相当于编译器生成了一个delegate,用以表示await语句之后的那些代码,并写入了相应的状态信息,用以确保await语句所等候的那项任务执行完毕以后这个delegate能够正确的得到调用。
这使得该方法看上去好像是从早前暂停的地方继续往下执行了,也就是所,系统会把状态恢复到早前暂停的样式,并且直接把程序中的某个线程放到适当的语句上,令其能够继续向下运行。
这个过程实际上是由SynchronizationContext类来实现的,该类用来保证异步方法能够在它所等候的任务执行完毕时,从早前停下来的地方继续往下运行,并确保该方法此时所处的环境与上下文能够与当初的情况一样。
总结
通过上面的讲述我们可以知道通过async与await关键字写出来的异步方法并没有太过神奇的地方。只不过编译器会针对这种方法生成许多代码,使得调用这个方法的主调方无需等待该方法完工,就可以继续往下执行,并确保该方法所等候的那项任务在执行过程中发生的错误能够适当的得到回报。这样的好处是,如果异步方法执行到await语句时它所要等候的那项任务还没有完成,那么该方法的执行进度就会暂停在那里,直到那项任务完成之后,才回继续往下执行。
希望这篇文章对你有所帮助,当然光了解异步没用,还要能够高效的编写异步代码才行哦,接下来我会抽时间讲讲进行异步开发的一些建议。当然我以前也写过相关的文章,你可以提前看下。同时欢迎大家加入.net core两千人交流群637326624`交流。当然我不会告诉你,关注公众号会第一时间收到文章推送。
很久没写文章了,生疏了后多,大家将就着看吧!
参考
《More Effective C#》机械工业出版社
依乐祝自己的理解
C#异步编程看这篇就够了的更多相关文章
- Python GUI之tkinter窗口视窗教程大集合(看这篇就够了)  JAVA日志的前世今生  .NET MVC采用SignalR更新在线用户数  C#多线程编程系列(五)- 使用任务并行库 C#多线程编程系列(三)- 线程同步 C#多线程编程系列(二)- 线程基础   C#多线程编程系列(一)- 简介
		
Python GUI之tkinter窗口视窗教程大集合(看这篇就够了) 一.前言 由于本篇文章较长,所以下面给出内容目录方便跳转阅读,当然也可以用博客页面最右侧的文章目录导航栏进行跳转查阅. 一.前言 ...
 - C#实现多级子目录Zip压缩解压实例  NET4.6下的UTC时间转换  [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了  asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程  asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案  .NET Core开发日志
		
C#实现多级子目录Zip压缩解压实例 参考 https://blog.csdn.net/lki_suidongdong/article/details/20942977 重点: 实现多级子目录的压缩, ...
 - Vue学习看这篇就够
		
Vue -渐进式JavaScript框架 介绍 vue 中文网 vue github Vue.js 是一套构建用户界面(UI)的渐进式JavaScript框架 库和框架的区别 我们所说的前端框架与库的 ...
 - .NET Core实战项目之CMS 第二章 入门篇-快速入门ASP.NET Core看这篇就够了
		
作者:依乐祝 原文链接:https://www.cnblogs.com/yilezhu/p/9985451.html 本来这篇只是想简单介绍下ASP.NET Core MVC项目的(毕竟要照顾到很多新 ...
 - React入门看这篇就够了
		
摘要: 很多值得了解的细节. 原文:React入门看这篇就够了 作者:Random Fundebug经授权转载,版权归原作者所有. React 背景介绍 React 入门实例教程 React 起源于 ...
 - net core体系-web应用程序-4asp.net core2.0 项目实战(CMS)-第二章 入门篇-快速入门ASP.NET Core看这篇就够了
		
.NET Core实战项目之CMS 第二章 入门篇-快速入门ASP.NET Core看这篇就够了 原文链接:https://www.cnblogs.com/yilezhu/p/9985451.ht ...
 - [转]React入门看这篇就够了
		
摘要: 很多值得了解的细节. 原文:React入门看这篇就够了 作者:Random Fundebug经授权转载,版权归原作者所有. React 背景介绍 React 入门实例教程 React 起源于 ...
 - ASP.NET Core WebApi使用Swagger生成api说明文档看这篇就够了
		
引言 在使用asp.net core 进行api开发完成后,书写api说明文档对于程序员来说想必是件很痛苦的事情吧,但文档又必须写,而且文档的格式如果没有具体要求的话,最终完成的文档则完全取决于开发者 ...
 - 想了解SAW,BAW,FBAR滤波器的原理?看这篇就够了!
		
想了解SAW,BAW,FBAR滤波器的原理?看这篇就够了! 很多通信系统发展到某种程度都会有小型化的趋势.一方面小型化可以让系统更加轻便和有效,另一方面,日益发展的IC**技术可以用更低的成本生产 ...
 
随机推荐
- 必知必会的JavaJDK工具
			
JDK中有很多用于监控诊断的系统工具,对于Java程序员来说,无疑是用来了解自己程序运行时性能好坏的强大工具. 在JDK的bin目录下就可以找到这些工具. JPS 在Linux有一个命令叫做ps,可以 ...
 - [TCP/IP] 学习TCP/IP协议的笔记
			
1.我看的视频是https://www.bilibili.com/video/av10610680?from=search&seid=1733008388243131444这位大大的视频讲解. ...
 - 一个ip, 两个域名, 两个ssl, 对应多个不同的项目  之   坑
			
之前配置了好几天, 就想通过tomcat直接配置. 找各种资料, 都说先配置Connector, 在配置Host. 我试了很多次, 都不成功. 原因我也没有找到在哪里. 我的配置参考如下网址: 修改这 ...
 - HTML中的表格标签
			
表格是网页制作中使用最多的工具之一,在制作网页时,使用表格可以更清晰地排列数据.但是在实际制作过程中,表格更多用在网页布局的定位上.很多网页都是以表格布局的.这是因为表格在文本和图像的位置控制方面 ...
 - 【前端知识体系-CSS相关】CSS特效实现之Transition和Transform对比
			
CSS效果 1.使用div绘制图形(三角形)? <!DOCTYPE html> <html lang="en"> <head> <meta ...
 - python:collections模块
			
Counter类 介绍:A counter tool is provided to support convenient and rapid tallies 构造:class collections. ...
 - 1142 CREATE VIEW command denied to user 'blog'@'XXX.XXX.XXX.XXX' for table 'Articles'
			
创建视图时,报如上的1142错误,是数据库权限设置的问题. 进入mysql的root用户,赋予所有权限即可: mysql>grant all privileges on blogDB.* to ...
 - SpringBoot源码学习系列之SpringMVC自动配置
			
目录 1.ContentNegotiatingViewResolver 2.静态资源 3.自动注册 Converter, GenericConverter, and Formatter beans. ...
 - 【论文阅读】Clustering Convolutional Kernels to Compress Deep Neural Networks
			
文章:Clustering Convolutional Kernels to Compress Deep Neural Networks 链接:http://openaccess.thecvf.com ...
 - javaScript——label语句
			
第一次看见label语句是这样一个场景: function foo() {x: 1} 当时十分疑惑,为什么不报错呢?对象可以这样写? 后来知道这个是label语句,一般配合break和continue ...