(给DotNet加星标,提升.Net技能)

转自:码农阿宇

cnblogs.com/CoderAyu/p/10680805.html

AsyncStreamsInCShaper 8.0

很开心今天能与大家一起聊聊C# 8.0中的新特性-Async Streams,一般人通常看到这个词表情是这样.

简单说,其实就是C# 8.0中支持await foreach.

或者说,C# 8.0中支持异步返回枚举类型async Task>.

好吧,还不懂?Good,这篇文章就是为你写的,看完这篇文章,你就能明白它的神奇之处了.

为什么写这篇文章

Async Streams这个功能已经发布很久了,在去年的Build 2018 The future of C#就有演示,最近VS 2019发布,在该版本的Release Notes中,我再次看到了这个新特性,因为对异步编程不太熟悉,所以借着这个机会,学习新特性的同时,把异步编程重温一遍.

Async / Await

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

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

常规示例

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

static int SumFromOneToCount(int count){    ConsoleExt.WriteLine("SumFromOneToCount called!");var sum = 0;for (var i = 0; i <= count; i++)    {        sum = sum + i;    }return sum;}

调用方法.

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("################################################");}

输出结果.

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

Yield Return

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

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;    }}

主函数

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);}

运行结果如下.

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

Async Return

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

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;}

主函数

static async Task Main(string[] args){const int count = 5;    ConsoleExt.WriteLine("async example starting.");// Sum runs asynchronously! Not enough. We need sum to be async with lazy behavior.var result = await SumFromOneToCountAsync(count);    ConsoleExt.WriteLine("async Result: " + result);    ConsoleExt.WriteLine("async completed.");    ConsoleExt.WriteLine("################################################");    ConsoleExt.WriteLine(Environment.NewLine);}

运行结果.

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

如果我们想把惰性枚举(yield return)与异步方法结合起来,即返回Task

Task

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

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

IAsyncEnumerable

其实,在C# 8.0中Task这种组合称为IAsyncEnumerable。

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

public interface IAsyncEnumerable{     IAsyncEnumerator GetAsyncEnumerator();}public interface IAsyncEnumerator : IAsyncDisposable{     Task<bool> MoveNextAsync();     T Current { get; }}// Async Streams Feature 可以被异步销毁public interface IAsyncDisposable{Task DiskposeAsync();}

AsyncStream

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

static async Task ConsumeAsyncSumSeqeunc(IAsyncEnumerable<int> sequence){    ConsoleExt.WriteLineAsync("ConsumeAsyncSumSeqeunc Called");await foreach (var value in sequence)    {        ConsoleExt.WriteLineAsync($"Consuming the value: {value}");// simulate some delay!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;// simulate some delay!await Task.Delay(TimeSpan.FromSeconds(0.5));yield return sum;    }}

主函数

static async Task Main(string[] args){const int count = 5;    ConsoleExt.WriteLine("Starting Async Streams Demo!");// Start a new task. Used to produce async sequence of data!    IAsyncEnumerable<int> pullBasedAsyncSequence = ProduceAsyncSumSeqeunc(count);// Start another task; Used to consume the async data sequence!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#");// Just for demo! Wait until the task is finished!await consumingTask;    ConsoleExt.WriteLineAsync("Async Streams Demo Done!");}

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

最后,看到这就是我们想要的结果,在枚举的基础上,进行了异步迭代.

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

Client/Server的异步拉取

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

同步调用

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

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

异步调用

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

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

Tips

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

需要安装.net core 3.0 preview的SDK(截至至博客撰写日期4月9日,.net core SDK最新版本为3.0.100-preview3-010431),安装好SDK后,如果你是VS 2019正式版,可能无法选择.net core 3.0,vs 2019 正式版默认情况下没有开启对预览版.net core 3.0的支持.

根据网友补充,需要在VS 2019正式版本中需要开启使用 .Net core SDK 预览版,才能创建3.0的项目.

工具 > 选项 > 项目和解决方案 > .Net Core > 使用 .Net core SDK 预览版

总结

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

Async Streams 背后的编程概念是异步拉取模型。我们请求获取序列的下一个元素,并最终得到答复。

Async Streams 提供了一种处理异步数据源的绝佳方法,希望对大家能够有所帮助。

文章中涉及的所有代码已保存在我的GitHub中,请尽情享用!

https://github.com/liuzhenyulive/AsyncStreamsInCShaper8.0

推荐阅读

(点击标题可跳转阅读)

C#8.0可空引用类型的使用注意要点

C#8.0和可为空引用类型

C#8.0新特性

看完本文有收获?请转发分享给更多人

关注「DotNet」加星标,提升.Net技能

喜欢就点一下「在看」呗~

c# hdf5 写string_聊一聊C#8.0中的 await foreach相关推荐

  1. 聊一聊C# 8.0中的await foreach

    很开心今天能与大家一起聊聊C# 8.0中的新特性-Async Streams,一般人通常看到这个词表情是这样. 简单说,其实就是C# 8.0中支持await foreach. 或者说,C# 8.0中支 ...

  2. linux手写数字识别,OpenCV 3.0中的SVM训练 mnist 手写字体识别

    前言: SVM(支持向量机)一种训练分类器的学习方法 mnist 是一个手写字体图像数据库,训练样本有60000个,测试样本有10000个 LibSVM 一个常用的SVM框架 OpenCV3.0 中的 ...

  3. [转]小心C# 5.0 中的await and async模式造成的死锁

    原文链接 https://www.cnblogs.com/OpenCoder/p/4434574.html 内容 UI Example Consider the example below. A bu ...

  4. Hhadoop-2.7.0中HDFS写文件源码分析(二):客户端实现(1)

    一.综述 HDFS写文件是整个Hadoop中最为复杂的流程之一,它涉及到HDFS中NameNode.DataNode.DFSClient等众多角色的分工与合作. 首先上一段代码,客户端是如何写文件的: ...

  5. php文件里直接写上?xml version=1.0 encoding=utf-8?出错?

    因为你的PHP启用了短标签功能,即<?php  ...  ?> 缩写为<? ... ?>,所以当你写上<?xml version="1.0" enco ...

  6. JS高级群的日常!写一个从10到0的倒计时,用console.log打印,不可以用 setInterval!本来说好的研究avalonJS最后演变成了看着大神在那边互相比拼实力。。...

    JS高级群的日常!写一个从10到0的倒计时,用console.log打印,不可以用 setInterval!本来说好的研究avalonJS最后演变成了看着大神在那边互相比拼实力.. 小森 执行一个函数 ...

  7. WinCE5.0中应用程序如何直接写屏

    以前曾利用GAPI实现应用程序直接操作显示驱动的FrameBuffer,以提高屏幕绘图和视频播放的效率.GAPI依赖于显示驱动,必须在显示驱动中添加相应的接口才能正常使用.如果平台是我们自己定制的,在 ...

  8. 阿里集团业务驱动的升级 —— 聊一聊Dubbo 3.0 的演进思路

    简介: 阿里云在 2020年底提出了"三位一体"理念,目标是希望将"自研技术"."开源项目"."商业产品"形成统一的技术 ...

  9. asp.net 2.0中的弹出对话框

    在asp.net 1.1中,要做1个弹出的对话框的话,一般是在服务端的代码中这样写: btnClick.Attributes.Add("onclick", "return ...

最新文章

  1. python3.8新特性 逻辑表达式_Python3.8新特性
  2. CentOS中JAVA_HOME的环境变量设置
  3. c语言程序转换成单片机语言,单片机编程常用到的类型转换 C语言程序实现
  4. 神经网络- receptive field
  5. 【期望】乘坐电梯(金牌导航 期望-2)
  6. 每个前端工程师都应该懂的前端性能优化总结:
  7. 通过Windows远程桌面连接将远程文件传输至本地
  8. 命令行_Laravel-admin artisan 命令行脚本使用
  9. Hive 整合 HBase
  10. 硬币支付问题(贪心策略)
  11. TD041S485H完全兼容ISO3080, ISO3086 ISO3082, ISO3088
  12. Android跑马灯进度条,table数据跑马灯效果
  13. MP1541升压电路
  14. AD网络标号高亮设置
  15. 山东标梵网站制作项目启动流程详解
  16. easyexcel使用教程-导出篇
  17. 使用Java建立一个公交管理系统,监督管理公交日常运营情况。
  18. 武汉大学 计算机学院周陌,计算机学院:披荆斩棘敢于拼 考研科班夺战绩
  19. C#由指定数据生成灰度位图或者彩色位图
  20. 基于java厨房管理系统_java食堂管理系统

热门文章

  1. 输入输出 原理 java_java输入输出,书写规范,运行原理,跨平台原理(复习)...
  2. SpringMVC-applicationContent.xml和Spring-servlet.xml的配置设置
  3. 3D视觉创新应用(三维重建)竞赛作品系列——多楼层室内环境下的三维几何重建
  4. Pandas的DataFrame数据类型
  5. ComplexHeatmap()函数解析
  6. 南京大学计算机2010年本科录取名单,2010高考本科录取名单
  7. pandas使用dropna函数删除dataframe中所有包含缺失值的数据行(drop rows which contain missing vlaues in dataframe)
  8. python使用np.linspace函数生成均匀的浮点数列表实战:生成浮点数列表、生成浮点数列表(指定是否包含末尾值)
  9. Python使用numpy函数hsplit水平(按列)拆分numpy数组(返回拆分后的numpy数组列表)实战:水平(按列)拆分二维numpy数组、split函数水平(按列)拆分二维numpy数组
  10. R语言将dataframe数据从宽表(wide)变为长表(long)实战:tidyr包的gather函数、cdata包的unpivot_to_blocks函数、data.table使用melt函数