.NET特性:异步流

自从VB/C#开始支持async/await后,开发者一直在期待异步版本的IEnumerable。但直到C# 7和ValueTask发布前,从性能的角度来看这一要求几乎是不可能实现的。

在老版本C#中,开发者每次使用await时都需要进行内存分配。如果要枚举10,000个项,则需要分配10,000个Task对象。就算使用任务缓存,这个数量也实在是太多了。通过使用ValueTask,可以只在某些情况下分配内存,此时IAsyncEnumerable<T>这种想法似乎也更可行了。

因此本文准备回顾一下2015年9月有关异步流的提议。

IAsyncEnumerable和IAsyncEnumerator

这一套接口是IEnumerable<T>的异步组件,不过通过下列方式进行了相应的简化:

public interface IAsyncEnumerable{public IAsyncEnumeratorGetEnumerator(); } public interface IAsyncEnumerator {  public T Current { get; }  public Task MoveNextAsync(); } 

如上所示,IEnumerator缺少Dispose或Reset方法。Reset的存在只是为了实现与COM的兼容性,如果真的有多个枚举器实现了这种方法,这种做法还是会让人感觉意外。Dispose大概会被取消,因为很多人认为,假设所有枚举器必然是的可释放的,这想法本身就是错的。

仅仅让MoveNext成为异步的,即可为我们带来两个收益:

Task<bool>的缓存要比Task<T>的缓存更容易,因此可减少内存分配量。

已经支持IEnumerator<T>的类只需要额外添加一个方法。

如上所述,“健谈”的异步库最大的问题就是内存分配。对于实现IAsyncEnumerator的类,这并不一定会成为问题:

假设正在对这样的异步序列进行foreach,并且在内部进行了缓冲,因此在99.9%的时间里,一个元素都会是本地可用并且同步可用的。如果正在await的Task已经完成,编译器会避免进行繁重的运算,并且会无需暂停直接从任务中获得所需的值。如果调用的特定方法内所有await的Task均已完成,那么所用方法绝对不会分配状态机,或通过委托存储为继续(Continuation),因为这些东西只会在首次需要时进行构造。

就算异步方法同步达到了return语句,由于await无需暂停,此时依然需要构造一个Task才能返回。因此一般来说这依然需要进行一次分配。然而编译器为该过程使用的助手API实际上会对已完成的Task缓存某些通用值,包括true和false。简而言之,针对已缓冲序列调用的MoveNextAsync以及调用方法通常什么都不会分配。

但如果数据并不一定被缓冲,此时又会怎样?

猜测:此时适合使用ValueTask或其他自定义的任务类型。理论上我们甚至可以提供一个“可重置的任务”,借此在枚举器调用MoveNextAsync时清除“已完成”标记。此类优化尚未进行过公开的讨论,甚至有可能是不可行的,不过C# 7开始考虑这个问题。

异步LINQ

回到2015提议,接下来要考虑的是LINQ。LINQ最大的问题在于synchronous/asynchronous源和synchronous/asynchronous委托之间组合的绝对数目。例如一个简单的Where函数可能需要四个重载(Overload):

public static IEnumerableWhere(this IEnumerable source, Func predicate); public static IAsyncEnumerable Where (this IAsyncEnumerable source, Func predicate); public static IAsyncEnumerable Where (this IEnumerable source, Func > predicate); public static IAsyncEnumerable Where (this IAsyncEnumerable source, Func > predicate); 

因此提议中提到:

因此我们要么需要将LINQ的外围应用翻四倍,要么需要为该语言引入某种新的隐式转换,例如从IEnumerable变为IAsyncEnumerable,或从Func变为Func>。这种做法值得考虑,但我们觉得也许可以通过某种方式让LINQ支持异步序列。

翻四倍的方法的影响可能被低估了,因为一些LINQ操作可能需要多个委托。

另一个问题是,到底要使用基于Task还是基于ValueTask的委托。2017年的一份文档提到了这个问题:“希望通过异步委托实现重载(可通过ValueTask提高效率)”。

语言支持

很明显,对于IAsyncEnumerable人们首先会考虑异步foreach,但如果代码中根本没有实际出现Task对象又会如何?这方面讨论过的选项包括:

foreach (string s in asyncStream) { ... } //implied await
await foreach (string s in asyncStream) { ... }
foreach (await string s in asyncStream) { ... }
foreach async (string s in asyncStream) { ... }
foreach await (string s in asyncStream) { ... }

同样的问题在于,执行诸如ConfigureAwait等操作时,从性能的角度考虑,库中的哪些东西是最重要的?如果不使用Task,又该如何进行ConfigureAwait?此时的最佳做法是同时向IAsyncEnumerable增加一个ConfigureAwait扩展方法。这样即可返回包装序列,进而返回包装枚举器,其MoveNextAsync可返回针对包装的枚举器中所包含的MoveNextAsync方法返回的任务调用ConfigureAwait后的结果:

该提议进一步谈到:

为此必须要让异步foreach像目前的同步foreach一样成为基于模式的(Pattern based),这样即可灵活调用任何GetEnumerator、MoveNext和Current成员,而无须考虑对象是否实现了正式的“接口”。这样做的原因在于Task.ConfigureAwait的结果并不是Task。

继续回到我们的猜测,这意味着一个类将可以在提供基于自定义枚举器的ValueTask等内容同时,继续支持IAsyncEnumerable<T>。这一点与List<T>的工作方式类似,可通过通用的IEnumerable<T>和基于结构(Struct)的备用枚举器实现更高性能。

取消令牌

接着是2017年1月的会议纪要,一起看看异步流。首先是一个有关取消令牌(Cancellation token)的棘手问题。

GetAsyncEnumerator能够接受可选的取消令牌,但具体是怎样做的?根据会议纪要:

  1. 使用另一个重载 :-(

  2. 使用一个默认参数 (CLS :-()

  3. 使用一个扩展方法(要求该扩展方法位于范围内)

有趣的是,尽管C#长期以来都支持默认参数,但CLS,即通用语言规范的约束依然是生效的。对于不熟悉这一概念的人可以这样理解:CLS定义了.NET平台上所有语言必须支持的最小功能集。另外,大部分库,尤其是基础库的令牌必须兼容CLS。

抛开具体API不谈,IAsyncEnumerable<T>获得取消令牌的方法就很明确了。但诸如Foreach block等迭代器如何获得取消令牌还不明确。他们正在考虑通过某种“走后门”的方法从状态机中得到令牌,但这可能需要修改枚举器的接口。

TryMoveNext?

继续看看之前提到的性能问题,如果可以在不进行异步调用的情况下检查是否已经具备可用数据,情况又会如何?

这正是添加bool? TryMoveNext()方法的理论依据。True/false可以按照预期工作,但如果获得了空值,则意味着需要调用MoveNextAsync来确定是否存在任何额外的数据。

此外也可考虑使用显式Chunking,其中每个调用可返回仅代表已缓冲数据的可枚举结果。

public IAsyncEnumerable> GetElementsAsync();

这些提议在供应方和消耗方目前都还存在一定的问题,因此据此决策尚未确定。

原文地址:http://www.infoq.com/cn/news/2017/05/async-streams


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

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

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

相关文章

MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established

Establishing SSL connection without server’s identity verification is not recommended. According to MySQL 5.5.45, 5.6.26 and 5.7.6 requirements SSL connection must be established by default if explicit option isn’t set. For compliance with existing appli…

优秀学生专栏——孙珩发

继优秀学生董超同学之后的孙珩发同学的回访录&#xff0c;孙珩发同学于今年5月份毕业&#xff0c;是一个非常非常懂事的孩子&#xff0c;比如让他帮忙拿一下水杯&#xff0c;一般的同学都是直接给你拿杯子过来&#xff0c;而孙珩发同学可不是&#xff0c;他会将水杯里面接满水&…

Java并发编程包中atomic的实现原理

转载自 Java并发编程包中atomic的实现原理 这是一篇来自粉丝的投稿&#xff0c;作者【林湾村龙猫】最近在阅读Java源码&#xff0c;这一篇是他关于并发包中atomic类的源码阅读的总结。Hollis做了一点点修改。 引子 在多线程的场景中&#xff0c;我们需要保证数据安全&#…

优秀学生专栏——王浩

今天继续回访优秀学生王浩&#xff0c;王浩是班级里学习最好的同学&#xff0c;就业的时候也是最早入职的&#xff0c;目前所处岗位是开发&#xff0c;最近在北京出差。企业多次向学校表扬王浩同学&#xff0c;以下是王浩同学的简单回访&#xff1a;想对学弟学妹说些什么&#…

.NET Framework 4.7正式发布

以前.NET Framework 4.7是随Windows 10 Creators Edition一并提供的&#xff0c;现在它已经正式发布&#xff0c;这意味着使用旧版本Windows的用户现在也能安装它了。.NET Framework 4.7通过Windows 10 Anniversary Update发布&#xff0c;支持Windows 7 SP1及以上版本&#xf…

Spring XML中如何使用 符号,比如数据库MySQL连接

<property name"url" value"jdbc:mysql://localhost:3306/spring?useUnicodetrue&amp;characterEncodingutf-8&amp;useSSLfalse"></property>使用&amp; 代替&符号&#xff0c;注意后面的分号

动态网站初识体验

一、静态网站的局限性&#xff1a; 1.无法实现交互功能 2.无法及时对页面的更新 二、动态网页&#xff1a; 1.可以根据不同的操作或者输入&#xff0c;返回不同的网页。 三、B/S&#xff1a;&#xff08;浏览器/服务器&#xff09; 程序完全部署在服务器上&#xff0c;用户通过…

如何高效排查系统故障?一分钱引发的系统设计“踩坑”案例

转载自 如何高效排查系统故障&#xff1f;一分钱引发的系统设计“踩坑”案例 背景说明 某日&#xff0c;做产品X的开发接到客户公司电话&#xff0c;说是对账出了1分钱的差错&#xff0c;无法处理。本着“客户第一”的宗旨&#xff0c;开发立马上线查看情况。查完发现&#…

JWT(JSON web token)

1.什么是JWT JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally sign…

优秀学生专栏——李浩然

今天回访的同学是李浩然同学&#xff0c;李浩然同学不光长得帅&#xff08;下面有照片哦&#xff09;&#xff0c;技术还过硬&#xff0c;今年5月份毕业的&#xff0c;目前从事教学工作&#xff0c;自从工作以来&#xff0c;企业曾多次向学校表扬李浩然同学&#xff0c;下面是对…

@ResponseBody导致的返回值中文乱码

新人学习springMVC开发框架&#xff0c;用到ajax 通过 ResponseBody 来获取返回值。 不得不说 ResponseBody的功能很强大&#xff0c;可以直接将返回类打包成json格式省却了很多事&#xff0c; 但是如果返回值是String类型的话&#xff0c;就会出现中文乱码问题&#xff0c;自…

一步步学习EF Core(1.DBFirst)

前言 很久没写博客了,因为真的很忙,终于空下来,打算学习一下EF Core顺便写个系列, 今天我们就来看看第一篇DBFirst. 本文环境:VS2017 Win7 .NET Core1.1 EF Core1.1.2 正文 这里我们不讨论是用DBFirst好,还是CodeFirst高端..各有各自的用处和适用场景.. 我们单纯的只是…

jsp数据交互(一)

一、jsp的内置对象&#xff1a; 1.out:out.print(“输出的内容”); 2.requset: (1)解决乱码&#xff1a;request.setCharacterEncoding(“utf-8”); (2)获取form表单里面的值&#xff1a; String name request.getParameter(“name”); 括号里面的参数是表单里面name的值。 (3…

JS中点击超链接但是不跳转

方式一 <td><a href"javascript:;">Delete</a></td>方式二 函数的返回值为false allA[i].onclick function(){var tr this.parentNode.parentNode;//var name tr.getElementsByTagName("td")[0].innerHTML;var name tr.child…

Greendao bean序列化出现的 问题!

报错&#xff1a; Found 1 problem(s) parsing "/home/zjs/Desktop/websocketTest/app/src/main/java/com/example/websockettest/dao/TerminalBean.java". First problem: Pb(96) The serializable class TerminalBean does not declare a static final serialVers…

ASP.NET Core改进了.NET Framework中的字符串处理

显然Microsoft开发人员和管理人员并没有表达清楚&#xff0c;事实上ASP.NET Core 2.0将会得到整个.NET Framework的支持。当前的更改只实现了在ASP.NET上提供.NET Core&#xff0c;这是为了便于开发而采取的一个临时步骤。对此&#xff0c;在ASP.NET Core预览发行声明中给出了如…

阿里P9谈程序员程序员的青春饭

转载自 阿里P9谈程序员程序员的"青春饭" 导读&#xff1a;你是否曾经认真思考过——毕业3-5年、10年&#xff0c;乃至更久后&#xff0c;我们希望成为什么样的人&#xff1f;作为一名技术人&#xff0c;我们要如何规划自己的职业发展生涯&#xff1f;网上热议的“…

优秀学生专栏——孙振涛

今天继续回访17级优秀毕业生&#xff0c;今天回访的同学是孙振涛同学&#xff0c;孙振涛在班内一直都是比较安稳守纪律&#xff0c;上学期间未违反过任何纪律&#xff0c;毕业之后自己创业合伙开了一个互联网公司&#xff0c;目前公司正在走上正轨&#xff0c;以下是对孙振涛同…

Android public class MyApplication extends MultiDexApplication使用

build.gradle(app)中设置 1. defaultConfig { multiDexEnabled true } 2. dependencies { compile ‘com.android.support:multidex:1.0.1’ } 3.使用 extends MultiDexApplication