.NET 异步,你也许不知道的5种用法

async/await异步操作,是C#中非常惊艳的“语法糖”,让异步编程变得优美且傻瓜化到了不可思议的程度。就连JavaScript都借鉴了async/await语法,让回调泛滥的JavaScript代码变得很优美。

我之前录制的.NET视频教程已经把async/await等基础知识介绍了,这篇文章不再介绍那些基础知识,如果有对它们还不了解的朋友,请到我的B站、头条、油管等平台搜索“杨中科 .net 教程”查看。

本篇文章只对在之前的视频教程中没有提到的几点做讲解。

 

用法1、控制并行执行的任务数量

       在项目开发的时候,有时候有很多任务需要异步执行,但是为了避免同时执行的异步任务太多,反而降低性能,因此通常需要限制并行执行的任务的数量。比如爬虫并行从网上抓取内容的时候,就要根据情况限制最大执行的线程的数量。

在没有async/await的年代,需要使用信号量等机制来进行线程间通讯来协调各个线程的执行,需要开发者对于多线程的技术细节非常了解。而使用async/await之后,这一切就可以变得非常傻瓜化了。

比如下面的代码用来首先从words.txt这个每行一个英文单词的字典中,逐个读取单词,然后调用一个API接口来获得单词的“音标、中文含义、例句”等详细信息。为了加快处理速度,需要采用异步编程来实现多任务同时下载,但是又要限制同时执行的任务的数量(假设为5个)。实现代码如下:

class Program
{static async Task Main(string[] args){ServiceCollectionservices = new ServiceCollection();services.AddHttpClient();services.AddScoped<WordProcessor>();using(var sp = services.BuildServiceProvider()){var wp = sp.GetRequiredService<WordProcessor>();string[]words = await File.ReadAllLinesAsync("d:/temp/words.txt");List<Task>tasks = new List<Task>();foreach(var word in words){tasks.Add(wp.ProcessAsync(word));if(tasks.Count==5){//waitwhen five tasks are readyawai tTask.WhenAll(tasks);tasks.Clear();}}//waitthe remnant which are less than five.await Task.WhenAll(tasks);}Console.WriteLine("done!");}
}class WordProcessor
{private IHttpClientFactory httpClientFactory;public WordProcessor(IHttpClientFactory httpClientFactory){this.httpClientFactory= httpClientFactory;}publicasync Task ProcessAsync(string word){Console.WriteLine(word);var httpClient = this.httpClientFactory.CreateClient();string json = await httpClient.GetStringAsync("http://dict.e.opac.vip/dict.php?sw="+ Uri.EscapeDataString(word));await File.WriteAllTextAsync("d:/temp/words/" + word + ".txt",json);}
}

 

核心代码就是下面这一段:

List<Task> tasks = newList<Task>();
foreach(var word in words)
{tasks.Add(wp.ProcessAsync(word));if(tasks.Count==5){//waitwhen five tasks are readyawait Task.WhenAll(tasks);tasks.Clear();}
}

这里遍历所有单词,抓取单词并且保存到磁盘的Process方法的返回值Task没有使用await关键字进行修饰,而是把返回的Task对象保存到list中,由于没有使用await进行等待,因此不用等一个任务执行完成,就可以把下一个任务加入list。当list中的任务满五个的时候,就调用await Task.WhenAll(tasks);等待这五个任务执行完成后,再处理下一组(5个)。循环之外的await Task.WhenAll(tasks);的是用来处理最后一组不足5个任务的情况。

 

用法2、在BackgroundService等异步执行的代码中进行DI注入

 

    依赖注入(DI)的时候,注入的对象都是有生命周期的。比如使用services.AddDbContext<TestDbContext>(...);这种方式注入EF Core中的DbContext的时候,TestDbContext的生命周期就是Scope。在普通的MVC的Controller中可以直接注入TestDbContext,但是在BackgroundService中是不能直接注入TestDbContext的。这时候,可以注入IServiceScopeFactory对象,然后在使用到TestDbContext对象的时候再调用IServiceScopeFactory的CreateScope()方法来生成一个IServiceScope,并且使用IServiceScope的ServiceProvider来手动解析获取TestDbContext对象。

代码如下:

public classTestBgService:BackgroundService
{private readonly IServiceScopeFactory scopeFactory;public TestBgService(IServiceScopeFactory scopeFactory){this.scopeFactory= scopeFactory;}protected override Task ExecuteAsync(CancellationToken stoppingToken){using(var scope = scopeFactory.CreateScope()){var sp = scope.ServiceProvider;var dbCtx = sp.GetRequiredService<TestDbContext>();foreach(var b in dbCtx.Books){Console.WriteLine(b.Title);}}               return Task.CompletedTask;}
}

 

用法3、异步方法可以不await

我在做youzack背单词的时候,有一个查询单词的功能。为了提升客户端的响应速度,我把每个单词的明细信息都按照“每个单词一个json文件”的形式,把单词的详细信息保存到文件服务器,相当于做了一个“静态化”。因此客户端在查询单词的时候,先到文件服务器中查找一下是否有对应的静态文件,如果有的话,就直接加载静态文件。如果在文件服务器不存在的话,再调用API接口的方法去查询,API接口从数据库中查询到单词后,不仅会把单词的详细信息返回给客户端,而且还会把单词的详细信息再上传到文件服务器。这样以后客户端再查询这个单词,就可以直接从文件服务器查询了。

因此API接口中“把从数据库中查询到的单词的详细信息上传到文件服务器”这个操作对于接口的请求者来讲没什么意义,而且会降低接口的响应速度,因此我就把“上传到文件服务器”这个操作写到了异步方法中,并且没有通过await来等待。

伪代码如下:

public async Task<WordDetail>FindWord(string word)
{var detail = await db.FindWordInDBAsync(word);//从数据库里查询_=storage.UploadAsync($”{word}.json”,detail.ToJsonString());//上传到文件服务器,但是不等待returnd etail;
}

 

在上面的UploadAsync调用中没有await调用等待,因此只要从数据库中查询出来,就把detail返回给请求者了,留下UploadAsync在异步线程中慢慢执行。

 

前面加的“_=”是消除对于不await异步方法造成编译器警告。

 

用法4、异步代码中Sleep的坑

 

    在编写代码的时候,有时候我们需要“暂停一段时间,再继续执行代码”。比如调用一个Http接口,如果调用失败,则需要等待2秒钟再重试。

    在异步方法中,如果需要“暂停一段时间”,那么请使用Task.Delay(),而不是Thread.Sleep(),因为Thread.Sleep()会阻塞主线程,就达不到“使用异步提升系统并发能力”的目的了。

如下代码是错误的:

public async Task<IActionResult> TestSleep()
{await System.IO.File.ReadAllTextAsync("d:/temp/words.txt");Console.WriteLine("firstdone");Thread.Sleep(2000);awaitSystem.IO.File.ReadAllTextAsync("d:/temp/words.txt");Console.WriteLine("seconddone");returnContent("xxxxxx");
}

上面的代码是能够正确的编译执行的,但是会大大降低系统的并发处理能力。因此要用Task.Delay()代替Thread.Sleep()。如下是正确的:

public async Task<IActionResult> TestSleep()
{awaitSystem.IO.File.ReadAllTextAsync("d:/temp/words.txt");Console.WriteLine("firstdone");awaitTask.Delay(2000);//!!!awaitSystem.IO.File.ReadAllTextAsync("d:/temp/words.txt");Console.WriteLine("seconddone");returnContent("xxxxxx");
}

 

用法5、yield如何用到异步方法中

    yield由于可以实现“产生一个数据就让IEnumerable的使用者处理一个数据”,从而实现数据处理的“流水线化”,提升数据处理的速度。

    但是,由于yield和async都是编译器提供的语法糖,编译器都会把它们修饰的方法编译为一个使用了状态机的类。因此两个语法糖碰到一起,编译器就迷惑了,因此不能直接在async修饰的异步方法中使用yield返回数据。

因此下面的代码是错误的:

static async IEnumerable<int>ReadCC()
{foreach(string line in await File.ReadAllLinesAsync("d:/temp/words.txt")){yieldreturn line.Length;}
}

只要把IEnumerable改成IAsyncEnumerable就可以了,如下是正确的:

static async IAsyncEnumerable<int>ReadCC()
{foreach(stringline in await File.ReadAllLinesAsync("d:/temp/words.txt")){yieldreturn line.Length;}
}

但是调用同时使用了async和yield的代码,不能使用普通的foreach+await,如下是错误的:

foreach (int i in await ReadCC())
{Console.WriteLine(i);
}

 

需要把await关键词移动到在foreach之前,如下是正确的:

await foreach(int i in ReadCC())
{Console.WriteLine(i);
}

编译器是微软写的,不知道为什么不支持foreach (int i in awaitReadCC())这样的写法,可能是由于为了兼容之前的C#语法规范不得已而为之吧。

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

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

相关文章

兼容FF,IE的纯CSS下拉菜单

代码简介&#xff1a;作者的CSS水平不错啊&#xff0c;值得我们学习&#xff0c;少了JS&#xff0c;就是好看多。 代码内容&#xff1a; <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transiti…

荐书 | 5本数学科普让你不再“畏惧”数学,感受数学的内在美

最近&#xff0c;小木了解了许多关于数学的书籍&#xff0c;简直打开了小木数学新世界的大门。出版社寄了一些样书给小木&#xff0c;经过斟酌对比之后&#xff0c;推荐以下5本数学科普书给大家。01《数学简史》[中] 蔡天新 43.50提到数学&#xff0c;很多人的第一反应就是复杂…

操作系统hpf算法事例_操作系统调度算法是什么

操作系统调度算法是什么导读&#xff1a;小编根据大家的需要整理了一份关于《操作系统调度算法是什么》的内容&#xff0c;具体内容&#xff1a;学习操作系统的朋友们肯定遇到过调度算法&#xff0c;目的是控制资源使用者的数量,选取资源使用者许可占用资源或占用资源&#xff…

ASP.NET Core依赖注入初识与思考

一、前言在上一篇中&#xff0c;我们讲述了什么是控制反转(IoC)以及通过哪些方式实现的。这其中&#xff0c;我们明白了&#xff0c;「控制反转&#xff08;IoC&#xff09;」 是一种软件设计的模式&#xff0c;指导我们设计出更优良&#xff0c;更具有松耦合的程序&#xff0c…

memcached mysql缓存_memcached做数据库缓存

最近研究memcache小有成果&#xff0c;把经验分享出来。白话:很早就听说memcache了&#xff0c;一直没搞懂&#xff0c;后来又看到redis很火&#xff0c;可以用来做缓存&#xff0c;研究了半天也没搞懂咋个做缓存&#xff0c;后来也不纠结了&#xff0c;继续学习python,当对pyt…

掌握Python爬虫基础,仅需1小时!

随着互联网的发展&#xff0c;google、百度等搜索引擎让我们获取信息愈加方便。但需求总会不断涌现&#xff0c;纯粹地借助百度等收集信息是远远不够的&#xff0c;因此编写爬虫爬取信息的重要性就越发凸显。比如有人为了炒股&#xff0c;专门爬取了多种股票信息&#xff1b;也…

我看ITIL在中国(六):如何建立有中国特色的IT运维管理平台【二】

在开始筹划建设有“中国特色的IT运维管理平台”之前&#xff0c;先来看看我们目前面临的情况&#xff1a; 随着信息化建设的不断深入&#xff0c;各种企业的核心业务都逐步地迁移到IT平台上来&#xff0c;对IT管理的要求也越来越高&#xff0c;IT需要管理&#xff0c;向IT管理要…

史上首次!世界杯使用视频裁判

2018年6月16日18时&#xff0c;法国队在喀山中央球场迎来了他们本届世界杯的首场比赛&#xff0c;对手是澳大利亚队。比赛进行到第56分钟&#xff0c;格列兹曼接到队友的直塞球&#xff0c;单刀杀入禁区&#xff0c;澳大利亚后卫里斯登铲球&#xff0c;但并没有碰到皮球&#x…

mysql 5.6.37 winx64_Mysql 5.6.37 winx64安装双版本mysql笔记记录

机器上现在已经存在5.0版本Mysql的情况下&#xff0c;继续安装一个最新版的mysql.一、官网下载免安装压缩包。本人下载的是mysql-5.6.37-winx64.zip.将压缩包解压到自定义目录中。例如:D:\mysql-5.6.37.二、添加环境变量.右键单击我的电脑->属性->高级系统设置(高级)->…

二分查找和折半插入排序一块说说-很合适~~~

前言上一篇在聊时间复杂度和空间复杂度时&#xff0c;没有按指定格式显示(明明预览的时候没问题的)&#xff0c;强迫症的我稍微优化了一下重新发布&#xff0c;目的就是让小伙伴看着舒服。上次聊到的直接插入排序在比较有序数据和待插入数据时&#xff0c;是通过依次遍历的方式…

用Python更加了解微信好友

用了微信几年了&#xff0c;微信号有也不少了&#xff0c;但是真正了解自己的好友吗&#xff1f;好友最多的城市是哪个&#xff1f;好友男女比例是多少&#xff1f;好友签名都是什么&#xff1f;今天我们来充分了解自己的微信好友。运行平台&#xff1a; Windows Python版本&a…

Linux下配置DNS

Linux下配置DNS一、配置环境1.Linux操作系统版本&#xff1a;RedHat AS 52.网络环境设置&#xff1a;IP&#xff1a;192.168.1.1 NetMark&#xff1a;255.255.255.0 Getway&#xff1a;192.168.1.13.软件包的准备&#xff1a;1&#xff09;bind-9.3.3-7.el5.i386.rpm …

python用pandas提取行列_python- pandas 不删除符合条件的行和列

我正在尝试建立一个回归模型,以便根据出现的单词来预测收视率(1-5)(回归本身并不一定表现良好,更多的是关于所采用的方法).我使用以下代码创建了一个词频矩阵&#xff1a;bow df.Review2.str.split().apply(pd.Series.value_counts)看起来像这样&#xff1a;我现在有兴趣删除在…

中国宜坚持发展自主操作系统

一直以来&#xff0c;我国IT产业存在“缺芯少魂”的问题&#xff0c;芯指的是芯片&#xff0c;魂指的是操作系统。操作系统是连接硬件和应用软件的媒介和桥梁。如果无法在操作系统方面实现安全可控&#xff0c;整个信息安全就无从谈起&#xff0c;而恰恰当下国内桌面操作系统市…

老板啥都懂,还天天套路我?!

今天我被老板叫到办公室进行了一场“推心置腹”的对话▼来源&#xff1a;特大号文章版权归原作者所有&#xff0c;转载仅供学习使用&#xff0c;不用于任何商业用途&#xff0c;如有侵权请留言联系删除&#xff0c;感谢合作。

如何提高电脑办公效能

其实你只要花一点点的时间&#xff0c;就可以使你的生活变得更好。在电脑办公上&#xff0c;记忆几个快捷键、安装一些实用的软件等绝对会让你的效率翻倍&#xff01; 在此弥缝分享几条我常常使用到的技巧和软件&#xff1a; 熟练一些快捷键 熟练的快捷键越多&#xff0c;做事越…

BeetleX.Http.Clients访问https服务

最近在做数据分析平台&#xff0c;那在做这个产品的时最需要的自然是测试数据&#xff0c;自己去构建行业测试数据比较麻烦&#xff0c;看到有同行产品的演示数据当然不能错过。由于采集过程中使用到BeetleX.Http.Clients去抓取第三方的Https接口数据&#xff0c;所以顺便记录一…

这一次,用数据解读玩家行为,用实力拿下预测大奖!

如今&#xff0c;大数据落地应用已十分广泛&#xff0c;除了政务、金融、医疗、旅游等传统行业外&#xff0c;在游戏行业中的应用也逐渐受人瞩目。那么&#xff0c;当大数据遇上游戏产业&#xff0c;会产生哪些变革和创新呢&#xff1f;众所周知&#xff0c;全球游戏市场规模庞…

如何在 C# 中使用隐式和显式操作符

C# 有一个鲜为人知的特性是通过定义 显式和隐式操作符 实现类型之间的转换&#xff0c;这篇文章我们将会讨论如何使用这些 显式 和 隐式 操作符。什么是显式&#xff0c;什么是隐式 隐式类型转换 它是运行时自动帮你完成的&#xff0c;言外之意就是你不需要人为干预&#xff0c…

SUSE10下配置FTP服务

SUSE10 liuux下配置FTP和SUSE9有不同之处&#xff0c;大家请注意。下面为SUSE10下ftp服务配置具体步骤。 Linux 系统安装完成后&#xff0c;默认不会开启FTP 服务&#xff0c;需要在yast界面下进行手动启动: 说明&#xff1a;如果未安装FTP 包&#xff0c;则需要先行安装FTP 软…