C#异步编程看这篇就够了

随着.NET Core的流行,相信你现在的代码中或多或少的会用到async以及await吧!毕竟已成标配。那么我们为什么要用async以及await呢?其实这是微软团队为我们提供的一个语法糖,让我们不用996就可以轻松的编写异步代码,并无太过神奇的地方。那么,问题来了,什么是异步?异步到底又是怎样的一个过程呢?

从一个故事说起

在开始讲异步前我们先从一个生活中的小故事说起吧。话说2019年12月15日周日这一天有位程序猿小祝在这天居然没有加班,选择在家休息了,然后他习惯性的用Microsoft To Do罗列了一下这天要做的事情,如下图所示:

这一天这个程序猿小祝计划早上九点起床洗澡,然后吃早餐,洗衣服,分享一篇关于C#异步相关的文章,晚上在家加下班~~没错,这个苦逼休息的时候也得工作,不然下周的任务有可能完不成要挨批了。

这个时候这个程序猿小祝可以选择,1.起床洗澡,2.吃早餐,3.洗衣服,4.写文章,5.打会球然后“远程写代码”。这个过程有严格的执行顺序,这个过程可以视为一个同步的过程。如下图所示:

当然,这个程序猿小祝却采用了另一种方式来进行:起床后先把衣服换下来用洗衣机洗了,然后开始洗澡,然后吃饭,写了一会文章,然后等衣服洗好后再把衣服给晾好继续回来写文章,最后在晚上的时候远程写代码。在这个过程中这个程序猿在洗衣服的同时就去洗澡,吃饭写了会文章了,这个过程就是一个异步的过程。

可能这个故事比喻的不恰当,不过大伙将就着看下吧,总结一下同步跟异步吧:

  1. 同步方法:可以认为程序是按照你写这些代码时所采用的顺序执行相关的指令的。

  2. 异步方法:可以在尚未完成所有指令的时候提前返回(如上面的洗衣服过程没执行完就返回去洗澡了),等到该方法等候的那项任务执行完毕后,在令这个方法从早前还没执行完的那个地方继续往下运行(如:衣服洗好晾好后,继续写文章了)。

下面我们结合伪代码来进行更加详细的讲解吧。

伪代码实例讲解

这一节我们就用伪代码来分别实现下同步过程及异步过程吧。

同步过程

下面我们用伪代码来实现上述故事中的过程吧。

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类来实现的,该类用来保证异步方法能够在它所等候的任务执行完毕时,从早前停下来的地方继续往下运行,并确保该方法此时所处的环境与上下文能够与当初的情况一样。

总结

通过上面的讲述我们可以知道通过asyncawait关键字写出来的异步方法并没有太过神奇的地方。只不过编译器会针对这种方法生成许多代码,使得调用这个方法的主调方无需等待该方法完工,就可以继续往下执行,并确保该方法所等候的那项任务在执行过程中发生的错误能够适当的得到回报。这样的好处是,如果异步方法执行到await语句时它所要等候的那项任务还没有完成,那么该方法的执行进度就会暂停在那里,直到那项任务完成之后,才会继续往下执行。

希望这篇文章对你有所帮助,当然光了解异步没用,还要能够高效的编写异步代码才行哦,接下来我会抽时间讲讲进行异步开发的一些建议。当然我以前也写过相关的文章,你可以提前看下。同时欢迎大家加入.net core两千人交流群637326624`交流。当然我不会告诉你,关注公众号会第一时间收到文章推送。

很久没写文章了,生疏了很多,大家将就着看吧!

参考

《More Effective C#》机械工业出版社

依乐祝自己的理解

好看你就点点我

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/312676.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sklearn PCA特征降维

机器学习中降维能够消除冗余&#xff0c;防止过拟合&#xff1b;发现潜在特征&#xff1b;减小计算量&#xff0c;节省资源和时间&#xff0c;常用的降维方法有&#xff1a;PCA &#xff0c;ICA&#xff0c;SOM&#xff0c;MDS&#xff0c; ISOMAP&#xff0c;LLE&#xff0c;本…

2019 AI Bootcamp·Guangzhou 参会日记

2019年的全球AI训练营在北京、上海、广州、杭州、宁波五个地方同时举办&#xff01;12月14日&#xff0c;微软全球AI Bootcamp活动再次驾临广州&#xff0c;本次会议结合 ML.NET 和基于 SciSharp 社区介绍最新的基于 .NET Core 人工智能技术&#xff0c;还有云端人工智能解决方…

LeetCode动态规划 杨辉三角

Given an integer numRows, return the first numRows of Pascal’s triangle. In Pascal’s triangle, each number is the sum of the two numbers directly above it as shown: 前几天做的动态规划题好难呜呜&#xff0c;做道水题找回自信~ 状态转移方程 triangle[i][j] …

AI Boot Camp 分享之 ML.NET 机器学习指南

今天在中国七城联动&#xff0c;全球134场的AI BootCamp胜利落幕&#xff0c;广州由卢建晖老师组织&#xff0c;我参与分享了一个主题《ML.NET 机器学习指南和Azure Kinect .NET SDK概要》&#xff0c;活动虽然只有短短的2天时间的宣传&#xff0c;报名70人&#xff0c;到场40多…

LeetCode动态规划 分割等和子集

Given a non-empty array nums containing only positive integers, find if the array can be partitioned into two subsets such that the sum of elements in both subsets is equal. 思路 这道题假如能看出是一道0-1背包问题的话&#xff0c;就很好解决了。题目说需要划分…

使用 Ocelot 匹配路由的方法匹配路由

使用 Ocelot 匹配路由的方法匹配路由Intro之前我们在 Ocelot 网关的基础上自定义了一个认证授权的 Ocelot 中间件&#xff0c;根据请求的路径和 Method 进行匹配&#xff0c;找到对应的权限配置&#xff0c;并判断是否可以拥有访问资源的角色&#xff0c;如果没有则返回 401/40…

【.NET Core 3.1】 策略授权中获取权限数据

▼更多精彩推荐&#xff0c;上午11点到达▼随着项目关注度渐渐升高&#xff0c;目前已经1.2k个star&#xff0c;我的内心反而更加的惶恐了起来&#xff0c;最近也是很有强迫症&#xff0c;只要有小伙伴反馈项目的问题&#xff0c;就很着急&#xff0c;哪怕一丁点的问题&#xf…

sklearn决策树概述

决策树是一类常见的机器学习方法&#xff0c;决策树学习的目的是为了产生一棵泛化能力强&#xff0c;即处理未见示例能力强的决策树。决策树是个递归生成的过程&#xff0c;如何选择最优划分属性是决策树学习的关键。我们希望决策树的分支节点所包含的样本尽可能属于同一类别&a…

使用ASP.NET Core 3.x 构建 RESTful API - 3.4 内容协商

现在&#xff0c;当谈论起 RESTful Web API 的时候&#xff0c;人们总会想到 JSON。但是实际上&#xff0c;JSON 和 RESTful API 没有半毛钱关系&#xff0c;只不过 JSON 恰好是RESTful API 结果的表述格式。也就是说 RESTful API 还可以使用其它的表述格式&#xff0c;例如 xm…

sklearn随机森林概述

随机森林是一个拓展变体&#xff0c;在以决策树为基学习器构建Bagging集成的基础上&#xff0c;进一步在决策树的训练过程中引入了随机属性选择。具体来说&#xff0c;传统决策树在选择划分属性时是在当前结点的属性集合中选择一个最优属性&#xff1b;而在随机森林的每个结点&…

《Dotnet9》系列-开源C# Winform控件库1《HZHControls》强力推荐

大家好&#xff0c;我是Dotnet9小编&#xff0c;一个从事dotnet开发8年的程序员。我最近在写dotnet分享文章&#xff0c;希望能让更多人看到dotnet的发展&#xff0c;了解更多dotnet技术&#xff0c;帮助dotnet程序员应用dotnet技术更好的运用于工作和学习中去。文章阅读导航一…

sklearn集成学习概述

常见的集成学习有Voting、Bagging、Boost和Stacking。 Voting代码 from sklearn.model_selection import train_test_split from sklearn.datasets import make_moons from sklearn.ensemble import RandomForestClassifier from sklearn.ensemble import VotingClassifier f…

不要叫我,我会叫你

之前看过前辈Artech关于控制反转的一篇文章&#xff0c;文章通俗易懂且言语精炼&#xff0c;写技术文章既是积累也是分享&#xff0c;既然是分享那么必须让读者能够明白到底讲解的什么&#xff0c;所以在这里我也挑战下自己&#xff0c;看看能不能将概念通过简洁代码和语言的形…

用.NET模拟天体运动

用.NET模拟天体运动这将是一篇罕见而偏极客的文章。我上大学时就见过一些模拟太阳系等天体运动的软件和网站&#xff0c;觉得非常酷炫&#xff0c;比如这个&#xff08;http://www.astronoo.com/en/articles/positions-of-the-planets.html&#xff09;&#xff1a; 其酷炫之处…

01 手把手带你构建大规模分布式服务--高并发、高可用架构系列,高质量原创好文!...

作者&#xff1a;丁浪&#xff0c;目前在创业公司担任高级技术架构师。曾就职于阿里巴巴大文娱和蚂蚁金服。具有丰富的稳定性保障&#xff0c;全链路性能优化的经验。架构师社区特邀嘉宾&#xff01;阅读本&#xff08;系列&#xff09;文章&#xff0c;你将会收获&#xff1a;…

如何正确的探索 Microsoft Ignite The Tour

Microsoft Ignite The Tour 是一年一度微软为全球开发者、IT专家、安全专家以及数据专家提供的为期两天&#xff0c;包含众多核心产品的实践性技术培训。2019.12.10-2019.12.11 已经在北京国家会议中心胜利闭幕&#xff0c;我作为一名Speaker 参与了两门课程的分享&#xff0c;…

Leetcode贪心 种花问题

You have a long flowerbed in which some of the plots are planted, and some are not. However, flowers cannot be planted in adjacent plots. Given an integer array flowerbed containing 0’s and 1’s, where 0 means empty and 1 means not empty, and an integer n…

回顾这一年,我沉默良久

今天是一个特殊的日子&#xff0c;因为还有一周就2024了。 回忆 我骑着我心爱的小电驴慢悠悠的走在下班的路上&#xff0c;看着万家灯火&#xff0c;匆匆而过的行人和那开着三轮车的摊贩们与城管斗智斗勇。 我陷入了回忆&#xff1f; 回忆着今年的进程&#xff0c;先是裁员…

Cookie、session、token对比

Cookiecookie 是一个非常具体的东西&#xff0c;指的就是浏览器里面能永久存储的一种数据&#xff0c;仅仅是浏览器实现的一种数据存储功能。cookie由服务器生成&#xff0c;发送给浏览器&#xff0c;浏览器把cookie以kv形式保存到某个目录下的文本文件内&#xff0c;下一次请求…

Leetcode贪心 验证回文字符串

Given a string s, return true if the s can be palindrome after deleting at most one character from it. 思路 用头尾指针遍历原字符串&#xff0c;但碰到所指不相同时&#xff0c;需要退出循环记录此书指针的位置。分别去除两个指针上的内容&#xff0c;查看删除一个字符…