.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,一经查实,立即删除!

相关文章

优秀学生专栏——孙珩发

继优秀学生董超同学之后的孙珩发同学的回访录&#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…

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

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

优秀学生专栏——李浩然

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

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

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

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;以下是对孙振涛同…

不可思议黑科技,Xamarin移动开发新时代

黑科技&#xff01;新一代Xamarin竟然可以将.NET代码原生编译成&#xff1a;Jar包供Java原生调用、swift类库、obj-c类库、C类库 供目标平台传统代码直接调用 之前和很多朋友聊到Xamarin觉得确实不错&#xff0c;原生性能&#xff0c;研发效率提升2倍&#xff0c;研发成本降低5…

这可能是最生动的加密相关科普文章

转载自 这可能是最生动的加密相关科普文章 谁都不想在通信过程中被别人“窃取”小秘密。本文借助一对情侣与八卦女、猥琐男的斗智故事&#xff0c;为大家讲述科普密码学基础知识。既有料又有趣&#xff0c;深入浅出&#xff0c;相信你会喜欢。 一、背景 事情是这样的&#…

学习心得——王梦茹

18级青鸟1班王梦茹编程其实是一门既枯燥又简单有乐趣的一门技术&#xff0c;这要根据个人而言&#xff0c;也许每个人来这里学习编程的初衷都是不一样的&#xff0c;但是你可以去慢慢的发现编程中的乐趣&#xff0c;兴趣是最大的老师&#xff0c;对感兴趣的事学习起来往往是事半…

JS中函数的返回值

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>01_数据类型</title> </head> <body> <!-- 1. 分类(2大类)* 基本(值)类型* Number: 任意数值* String: 任意文本* Boolean: true…

学习心得——李嫣然、逯广捷

18级青鸟1班李嫣然、逯广捷我觉得学习中最基本的一点&#xff0c;就是主动&#xff0c;学习是自己的事情&#xff0c;只有积极主动地学习才能感受到学习中的乐趣&#xff0c;要是越烦越不想学那肯定就是更学不会&#xff0c;这样周而复始最后就是什么都不会&#xff0c;所以说学…

一步步学习EF Core(2.事务与日志)

前言 上节我们留了一个问题,为什么EF Core中,我们加载班级,数据并不会出来 其实答案很简单,~ 因为在EF Core1.1.2 中我们在EF6.0中用到的的延迟加载功能并没有被加入,不过在EF Core 2.0中,这个功能将回归 而且这个功能是否需要被加入进去,社区也在激烈的讨论当中,有兴趣的可…

学习心得——高婕

18级青鸟1班高婕在这里和大家谈谈我学习的心得体会。我认为&#xff0c;要把学习当作是一种乐趣&#xff0c;不要当作一种负担。作为一名学生&#xff0c;我们要尊重老师的辛勤劳动&#xff0c;认真听好每一节课。其实在这里学习编程很简单&#xff0c;跟着老师的步调走&#x…

JS中对象的总结

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>03_对象</title> </head> <body> <!-- 1. 什么是对象?* 代表现实中的某个事物, 是该事物在编程中的抽象* 多个数据的集合体(封装…

Visual Studio 2017 针对移动开发的新特性介绍

Visual Studio是世界上最好的IDE之一&#xff0c;如果是 .NET世界&#xff0c;那就没有之一了(^_^)&#xff0c;而最近推出的Visual Studio 2017在移动平台方面更是加强了这一点。这个版本包含了一些非常棒的功能&#xff0c;包括实时单元测试、新的重构、代码提示、C&#xff…