聊一聊C# 8.0中的await foreach

很开心今天能与大家一起聊聊C# 8.0中的新特性-Async Streams,一般人通常看到这个词表情是这样.
640?wx_fmt=jpeg
简单说,其实就是C# 8.0中支持await foreach.
640?wx_fmt=jpeg
或者说,C# 8.0中支持异步返回枚举类型async Task<IEnumerable<T>>.
640?wx_fmt=jpeg
好吧,还不懂?Good,这篇文章就是为你写的,看完这篇文章,你就能明白它的神奇之处了.

为什么写这篇文章

Async Streams这个功能已经发布很久了,在去年的Build 2018 The future of C#就有演示,最近VS 2019发布,在该版本的Release Notes中,我再次看到了这个新特性,因为对异步编程不太熟悉,所以借着这个机会,学习新特性的同时,把异步编程重温一遍.
本文内容,参考了Bassam Alugili在InfoQ中发表的Async Streams in C# 8,撰写本博客前我已联系上该作者并得到他支持.

Async / Await

C# 5 引入了 Async/Await,用以提高用户界面响应能力和对 Web 资源的访问能力。换句话说,异步方法用于执行不阻塞线程并返回一个标量结果的异步操作。

微软多次尝试简化异步操作,因为 Async/Await 模式易于理解,所以在开发人员当中获得了良好的认可。

详见The Task asynchronous programming model in C#

常规示例

要了解问什么需要Async Streams,我们先来看看这样的一个示例,求出5以内的整数的和.

Copy

static int SumFromOneToCount(int count)
{
ConsoleExt.WriteLine("SumFromOneToCount called!");

var sum = 0;
for (var i = 0; i <= count; i++)
{
sum = sum + i;
}
return sum;
}

调用方法.

Copy

static void Main(string[] args)
{
const int count = 5;
ConsoleExt.WriteLine($"Starting the application with count: {count}!");
ConsoleExt.WriteLine("Classic sum starting.");
ConsoleExt.WriteLine($"Classic sum result: {SumFromOneToCount(count)}");
ConsoleExt.WriteLine("Classic sum completed.");
ConsoleExt.WriteLine("################################################");
}

输出结果.

640?wx_fmt=png

可以看到,整个过程就一个线程Id为1的线程自上而下执行,这是最基础的做法.

Yield Return

接下来,我们使用yield运算符使得这个方法编程延迟加载,如下所示.

Copy

static IEnumerable<int> SumFromOneToCountYield(int count)
{
ConsoleExt.WriteLine("SumFromOneToCountYield called!");

var sum = 0;
for (var i = 0; i <= count; i++)
{
sum = sum + i;

yield return sum;
}
}

主函数

Copy

static void Main(string[] args)
{
const int count = 5;
ConsoleExt.WriteLine("Sum with yield starting.");
foreach (var i in SumFromOneToCountYield(count))
{
ConsoleExt.WriteLine($"Yield sum: {i}");
}
ConsoleExt.WriteLine("Sum with yield completed.");

ConsoleExt.WriteLine("################################################");
ConsoleExt.WriteLine(Environment.NewLine);
}

运行结果如下.

640?wx_fmt=png

正如你在输出窗口中看到的那样,结果被分成几个部分返回,而不是作为一个值返回。以上显示的累积结果被称为惰性枚举。但是,仍然存在一个问题,即 sum 方法阻塞了代码的执行。如果你查看线程ID,可以看到所有东西都在主线程1中运行,这显然不完美,继续改造.

Async Return

我们试着将async用于SumFromOneToCount方法(没有yield关键字).

Copy

static async Task<int> SumFromOneToCountAsync(int count)
{
ConsoleExt.WriteLine("SumFromOneToCountAsync called!");

var result = await Task.Run(() =>
{
var sum = 0;

for (var i = 0; i <= count; i++)
{
sum = sum + i;
}
return sum;
});

return result;
}

主函数.

Copy

static async Task Main(string[] args)
{
const int count = 5;
ConsoleExt.WriteLine("async example starting.");

var result = await SumFromOneToCountAsync(count);
ConsoleExt.WriteLine("async Result: " + result);
ConsoleExt.WriteLine("async completed.");

ConsoleExt.WriteLine("################################################");
ConsoleExt.WriteLine(Environment.NewLine);
}

运行结果.

640?wx_fmt=png

我们可以看到计算过程是在另一个线程中运行,但结果仍然是作为一个值返回!任然不完美.

如果我们想把惰性枚举(yield return)与异步方法结合起来,即返回Task<IEnumerable,这怎么实现呢?

Task<IEnumerable>

我们根据假设把代码改造一遍,使用Task<IEnumerable<T>>来进行计算.

640?wx_fmt=png

可以看到,直接出现错误.

IAsyncEnumerable

其实,在C# 8.0中Task<IEnumerable>这种组合称为IAsyncEnumerable。这个新功能为我们提供了一种很好的技术来解决拉异步延迟加载的问题,例如从网站下载数据或从文件或数据库中读取记录,与 IEnumerable 和 IEnumerator 类似,Async Streams 提供了两个新接口 IAsyncEnumerable 和 IAsyncEnumerator,定义如下:

Copy

public interface IAsyncEnumerable<out T>
{
IAsyncEnumerator<T> GetAsyncEnumerator();
}

public interface IAsyncEnumerator<out T> : IAsyncDisposable
{
Task<bool> MoveNextAsync();
T Current { get; }
}


public interface IAsyncDisposable
{
Task DiskposeAsync();
}

AsyncStream

下面,我们就来见识一下AsyncStrema的威力,我们使用IAsyncEnumerable来对函数进行改造,如下.

Copy

static async Task ConsumeAsyncSumSeqeunc(IAsyncEnumerable<int> sequence)
{
ConsoleExt.WriteLineAsync("ConsumeAsyncSumSeqeunc Called");

await foreach (var value in sequence)
{
ConsoleExt.WriteLineAsync($"Consuming the value: {value}");


await Task.Delay(TimeSpan.FromSeconds(1));
};
}

private static async IAsyncEnumerable<int> ProduceAsyncSumSeqeunc(int count)
{
ConsoleExt.WriteLineAsync("ProduceAsyncSumSeqeunc Called");
var sum = 0;

for (var i = 0; i <= count; i++)
{
sum = sum + i;


await Task.Delay(TimeSpan.FromSeconds(0.5));

yield return sum;
}
}

主函数.

Copy

static async Task Main(string[] args)
{
const int count = 5;
ConsoleExt.WriteLine("Starting Async Streams Demo!");


IAsyncEnumerable<int> pullBasedAsyncSequence = ProduceAsyncSumSeqeunc(count);


var consumingTask = Task.Run(() => ConsumeAsyncSumSeqeunc(pullBasedAsyncSequence));

await Task.Delay(TimeSpan.FromSeconds(3));

ConsoleExt.WriteLineAsync("X#X#X#X#X#X#X#X#X#X# Doing some other work X#X#X#X#X#X#X#X#X#X#");


await consumingTask;

ConsoleExt.WriteLineAsync("Async Streams Demo Done!");
}

如果一切顺利,那么就能看到这样的运行结果了.

640?wx_fmt=png

最后,看到这就是我们想要的结果,在枚举的基础上,进行了异步迭代.
可以看到,整个计算过程并没有造成主线程的阻塞,其中,值得重点关注的是红色方框区域的线程5!线程5!线程5!线程5在请求下一个结果后,并没有等待结果返回,而是去了Main()函数中做了别的事情,等待请求的结果返回后,线程5又接着执行foreach中任务.

Client/Server的异步拉取

如果还没有理解Async Streams的好处,那么我借助客户端 / 服务器端架构是演示这一功能优势的绝佳方法。

同步调用

客户端向服务器端发送请求,客户端必须等待(客户端被阻塞),直到服务器端做出响应.

640?wx_fmt=jpeg

示例中Yield Return就是以这种方式执行的,所以整个过程只有一个线程即线程1在处理.

异步调用

客户端发出数据块请求,然后继续执行其他操作。一旦数据块到达,客户端就处理接收到的数据块并询问下一个数据块,依此类推,直到达到最后一个数据块为止。这正是 Async Streams 想法的来源。

640?wx_fmt=jpeg

最后一个示例就是以这种方式执行的,线程5询问下一个数据后并没有等待结果返回,而是去做了Main()函数中的别的事情,数据到达后,线程5又继续处理foreach中的任务.

Tips

如果你使用的是.net core 2.2及以下版本,会遇到这样的报错.

640?wx_fmt=png

需要安装.net core 3.0 preview的SDK(截至至博客撰写日期4月9日,.net core SDK最新版本为3.0.100-preview3-010431),安装好SDK后,如果你是VS 2019正式版,可能无法选择3.0的与预览版,听过只有VS 2019 Preview才支持.Net core 3.0的预览版.

640?wx_fmt=png

总结

我们已经讨论过 Async Streams,它是一种出色的异步拉取技术,可用于进行生成多个值的异步计算。

Async Streams 背后的编程概念是异步拉取模型。我们请求获取序列的下一个元素,并最终得到答复。Async Streams 提供了一种处理异步数据源的绝佳方法,希望对大家能够有所帮助。

文章中涉及的所有代码已保存在我的GitHub中,请尽情享用!
https://github.com/liuzhenyulive/AsyncStreamsInCShaper8.0

致谢

之前一直感觉国外的大师级开发者遥不可及甚至高高在上,在遇到Bassam Alugili之后,我才真正感受到技术交流没有高低贵贱,正如他对我说的 The most important thing in this world is sharing the knowledge!
Thank you,I will keep going!!

参考文献: Async Streams in C# 8 https://www.infoq.com/articles/Async-Streams

原文地址: https://www.cnblogs.com/CoderAyu/p/10680805.html

.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com 
640?wx_fmt=jpeg

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

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

相关文章

ASP.NET Core 实现带认证功能的Web代理服务器

引言最近在公司开发了一个项目&#xff0c;项目部署架构图如下&#xff1a;思路如图中文本所述&#xff0c;公司大数据集群不允许直接访问外网&#xff0c;需要一个网关服务器代理请求&#xff0c;本处服务器A就是边缘代理服务器的作用。通常技术人员最快捷的思路是在服务器A上…

Unfair contest(个人做法)

Unfair contest 题意&#xff1a; 两个人参赛&#xff0c;n个评委打分&#xff0c;去掉s个最高分&#xff0c;去掉t个最低分&#xff0c;剩下分求平均分&#xff0c;平均分大的获胜。你是第n个评委&#xff0c;此时已知前n-1个评委所打分数&#xff0c;现在轮到你打分&#x…

ASP.NET Core 进程外(out-of-process)托管(7)《从零开始学ASP.NET CORE MVC》

本文出自《从零开始学ASP.NET CORE MVC》推荐文章&#xff1a;ASP.NET Core 进程内(InProcess)托管ASP.NET Core 进程内(InProcess)托管我们先简单回顾下 ASP.NET Core 中,要配置InProcess的服务器&#xff0c;需要在项目文件中添加< AspNetCoreHostingModel >元素&#…

eShopOnContainers 知多少[10]:部署到 K8S | AKS

1. 引言断断续续&#xff0c;感觉这个系列又要半途而废了。趁着假期&#xff0c;赶紧再更一篇&#xff0c;介绍下如何将eShopOnContainers部署到K8S上&#xff0c;进而实现大家常说的微服务上云。2. 先了解下 Helm读过我上篇文章ASP.NET Core 借助 K8S 玩转容器编排的同学&…

DI是实现面向切面和面向抽象的前提

DI越来越重要DI就是依赖注入&#xff0c;现在来说&#xff0c;大部分框架都是以DI为基础组件的&#xff0c;每一个框架都有自己的DI组件&#xff0c;像dotnet core&#xff0c;java spring等&#xff0c;也都为自己的框架量身打造了DI工具。面向对象的几个原则依赖倒置原则&…

.net core 并发下的线程安全问题

抱歉&#xff0c;其实内容并不如题&#xff01;&#xff01;&#xff01;背景&#xff08;写测试demo所出现的异常&#xff0c;供大家学习与拍砖&#xff09;&#xff1a;.net core webapi项目&#xff0c;做了一个授权的filter&#xff08;真正的生产项目的话&#xff0c;JWT很…

cf1555B. Two Tables

cf1555B. Two Tables 题意&#xff1a; 一个大矩阵空间内放置一个矩阵a&#xff0c;现在要再往这个空间内放一个矩阵b&#xff0c;a移动距离len才能放下b&#xff0c;问len最小是多少 题解&#xff1a; 不难发现左右或上下移动是最佳的&#xff0c;斜着移动是最不好的。此时…

cf1555C Coin Rows

cf1555C Coin Rows 题意&#xff1a; 有一个两行m列的地图&#xff0c;每个格子都有对应的价值&#xff0c;有a&#xff0c;b两个人&#xff0c;都从左上角到右下角&#xff0c;且都只能向右向下走&#xff0c;a先出发&#xff0c;a每到一个格子&#xff0c;就会获得这个地方…

C#并行编程(2):.NET线程池

线程 Thread在总结线程池之前&#xff0c;先来看一下.NET线程。.NET线程与操作系统(Windows)线程有什么区别&#xff1f;.NET利用Windows的线程处理功能。在C#程序编写中&#xff0c;我们首先会新建一个线程对象System.Threading.Thread&#xff0c;并为其指定一个回调方法&…

ASP.NET Core launchsettings.json文件(8)《从零开始学ASP.NET CORE MVC》:

本文出自《从零开始学ASP.NET CORE MVC》推荐文章&#xff1a;ASP.NET Core 进程外(out-of-process)托管ASP.NET Core launchsettings.json文件在本视频中&#xff0c;我们将讨论在ASP.NET Core项目中launchsettings.json文件的重要性。launchsettings.json文件您将在项目根文件…

[CQOI2017] 老C的方块(网络流染色建图)

problem luogu-P3756 solution 据说要做网络流 24\text{24}24 题中的《方格取数问题》和《骑士共存问题》。 &#xff1f;&#xff1f;&#xff1f;那个不是直接最小割吗&#xff1f;哦原来是从黑白染色来理解的。我还是太水了。 这种题之所以能用网络流做&#xff0c;是因…

江湖召集:.NET开发者们看过来,这场长沙的开发者技术大会正是为你精心准备的大餐...

看过去&#xff0c;历史的尘埃与沧海桑田古语有云“近代中国&#xff0c;湖南独撑半边天”&#xff0c;湖南长沙&#xff0c;作为湖南省的省会&#xff0c;自古以来便是各界风云人士兴起之地。随着互联网时代的到来&#xff0c;长沙&#xff0c;这座历史悠久的文化名城&#xf…

C#并行编程(3):并行循环

初识并行循环并行循环主要用来处理数据并行的&#xff0c;如&#xff0c;同时对数组或列表中的多个数据执行相同的操作。在C#编程中&#xff0c;我们使用并行类System.Threading.Tasks.Parallel提供的静态方法Parallel.For和Parallel.ForEach来实现并行循环。从方法名可以看出&…

Acwing 252. 树

Acwing 252. 树 题意&#xff1a; 给定一个有 N 个点&#xff08;编号 0,1,…,N−1&#xff09;的树&#xff0c;每条边都有一个权值&#xff08;不超过 1000&#xff09;。 树上两个节点 x 与 y 之间的路径长度就是路径上各条边的权值之和。 求长度不超过 K 的路径有多少条…

.net 4.5部署到docker容器

.NET FX 应用程序也是可以容器化的&#xff0c;容器化的选项有两个&#xff1a;部署到windows容器部署到linux容器部署到windows容器由于.net本身就是运行在windows平台的&#xff0c;所以它与windows容器也是更加适合&#xff0c;你可以以iis镜像为基础&#xff0c;去编写你的…

将传统 WPF 程序迁移到 DotNetCore 3.0

介绍由于历史原因&#xff0c;基于 Windows 平台存在着大量的基于 .NetFramework 开发的 WPF 和 WinForm 相关程序&#xff0c;如果将这些程序全部基于 DotNetCore 3.0 重写一遍显然是不现实的&#xff0c;但是 DotNetCore 是未来发展的趋势。所以本文通过以 WPF 为例&#xff…

.NET Core 时代已经到了,你准备好了吗

今天很多人都收到了阿里云函数计算支持.NET Core的短信了。通过访问 https://help.aliyun.com/document_detail/112379.html 你可以看到最新的说明。现在和过去的两年不同&#xff0c;因为最恶劣的时期已经过去&#xff0c;经历过了最黑暗的时刻&#xff0c;我们正在走向光明的…

ASP.NET Core appsettings.json文件(9)《从零开始学ASP.NET CORE MVC》:

本文出自《从零开始学ASP.NET CORE MVC》推荐文章&#xff1a;ASP.NET Core launchsettings.json文件ASP.NET Core appsettings.json文件在本视频中&#xff0c;我们将讨论ASP.NET Core 项目中appsettings.json文件的重要性。在以前的ASP.NET版本中&#xff0c;我们将应用程序配…

在Windows上使用Docker运行.NetCore

今天我们来说下如何在windows下使用docker运行.net core&#xff0c;既然是docker&#xff0c;那么我们首先得在windows上安装docker。在Windows安装 docker 有两种选择 &#xff1a;1、docker for windows2、docker toolbox 区别&#xff1a;docker for windows-64位Windows 1…

浅谈C#在网络波动时防重复提交

前几天&#xff0c;公司数据库出现了两条相同的数据&#xff0c;而且时间相同&#xff08;毫秒也相同&#xff09;。排查原因&#xff0c;发现是网络波动造成了重复提交。由于网络波动而重复提交的例子也比较多&#xff1a;网络上&#xff0c;防重复提交的方法也很多&#xff0…