C# 8 中的异步迭代器 IAsyncEnumerable<T> 解析
异步编程已经流行很多年了,.NET 引入的 async 和 await 关键词让异步编程更具有可读性,但有一个遗憾,在 C# 8 之前都不能使用异步的方式处理数据流,直到 C# 8 引入的 IAsyncEnumerable<T> 才解决了这个问题。
说到 IAsyncEnumerable<T> ,得先说一说 IEnumerable<T> ,大家都知道,它是用同步的方式来迭代 collection 集合的,而这里的 IAsyncEnumerable<T> 则是用异步方式,换句话说: IAsyncEnumerable<T> 在迭代集合的过程中不会阻塞调用线程。
IAsyncDisposable, IAsyncEnumerable<T>, IAsyncEnumerator<T>
异步迭代器 允许我们可以用异步的方式处理数据,在这之前要了解下面三个接口:IAsyncDisposable, IAsyncEnumerable<T> 和 IAsyncEnumerator<T>,他们都是在 .NET Standard 2.1 中被引入,下面的代码片段展示了这三个接口的定义。
public interface IAsyncDisposable
{
ValueTask DisposeAsync();
}
public interface IAsyncEnumerable<out T>
{
IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken
token = default);
}
public interface IAsyncEnumerator<out T> : IAsyncDisposable
{
ValueTask<bool> MoveNextAsync();
T Current { get; }
}
为什么要使用异步迭代器
可以想象一下你有一个数据访问层需要从数据库中一次性读取所有的数据,要想使用这个功能很简单,可以直接调用 底层提供的异步方法 XXXAsyc 实现异步调用并且一次性返回所有数据。
只要不是将所有数据都呈现在页面上的话,这种解决方案问题不是太大,很多时候更多的是通过 分页读取 的形式,其实在这方面还有一个比较好的做法就是在数据可用时立即返回给调用者。
准确的说,这里可使用 异步迭代器 的方式来解决,如果你的方法是同步返回的话,你可以使用 return yield + 返回值 IEnumerable<T> 模式,很遗憾的是,这种方式没有扩展性,因为它是需要阻塞调用线程的。
最好的解决方案就是 return yield + 返回值 IAsyncEnumerable<T> 模式,异步迭代器方法返回的是 IAsyncEnumerable<T>实例,并且可以包含一个或多个 yield return 语句。
在 C#8 中创建异步迭代器
下面的代码片段展示了一个返回 Task<IEnumerable<T>> 类型的异步方法,如下代码所示:
class Program
{
const int DELAY = 1000;
const int MIN = 1;
const int MAX = 10;
public static async Task Main(string[] args)
{
foreach (int number in await GetData())
{
Console.WriteLine($"{DateTime.Now}: number={number}");
}
Console.ReadLine();
}
public static async Task<IEnumerable<int>> GetData()
{
List<int> integers = new List<int>();
for (int i = MIN; i <= MAX; i++)
{
await Task.Delay(DELAY);
integers.Add(i);
}
return integers;
}
}
当运行上面的应用程序,它会等待 10s 之后再将所有的 1-10 的数字输出控制台上,虽然这个 GetData 是异步的,但最终还是一次性输出了,而不是一个一个的隔秒输出。
这个时候可以让 yield 关键词介入,它是在 C# 2.0 中被引入的,常用于执行状态迭代 并且按一个一个的从集合中返回数据,你不需要像上面一样创建一个集合(integers) 再返回上去,下面的代码片段是修改 GetData 方法并且合并了 yield 关键词的版本,代码如下:
static async IAsyncEnumerable<int> GetData()
{
for (int i = MIN; i < MAX; i++)
{
yield return i;
await Task.Delay(DELAY);
}
}
C#8 中使用异步迭代器
要想使用异步流, 需要在 foreach 前增加一个 await 关键词,如下代码所示:
public static async Task Main(string[] args)
{
await foreach (int number in GetData())
{
Console.WriteLine($"{DateTime.Now}: number={number}");
}
Console.ReadLine();
}
下面是完整的仅供参考的代码。
class Program
{
const int DELAY = 1000;
const int MIN = 1;
const int MAX = 10;
public static async Task Main(string[] args)
{
await foreach (int number in GetData())
{
Console.WriteLine($"{DateTime.Now}: number={number}");
}
Console.ReadLine();
}
static async IAsyncEnumerable<int> GetData()
{
for (int i = MIN; i < MAX; i++)
{
yield return i;
await Task.Delay(DELAY);
}
}
}
C# 8 中一个非常重要的特性就是支持了 IAsyncEnumerable<T>,它可以让你应用程序代码更干净,更高效 和 更高性能。
更多精彩,欢迎订阅

译文链接:https://www.infoworld.com/article/3531251/how-to-use-asynchronous-streams-in-csharp-80.html
C# 8 中的异步迭代器 IAsyncEnumerable<T> 解析的更多相关文章
- 一文说通C#中的异步迭代器
今天来写写C#中的异步迭代器 - 机制.概念和一些好用的特性 迭代器的概念 迭代器的概念在C#中出现的比较早,很多人可能已经比较熟悉了. 通常迭代器会用在一些特定的场景中. 举个例子:有一个for ...
- 深入Asyncio(八)异步迭代器
Async Iterators: async for 除了async def和await语法外,还有一些其它的语法,本章学习异步版的for循环与迭代器,不难理解,普通迭代器是通过__iter__和__ ...
- [译]Python中的异步IO:一个完整的演练
原文:Async IO in Python: A Complete Walkthrough 原文作者: Brad Solomon 原文发布时间:2019年1月16日 翻译:Tacey Wong 翻译时 ...
- 【JS】336- 拆解 JavaScript 中的异步模式
点击上方"前端自习课"关注,学习起来~ JavaScript 中有很多种异步编程的方式.callback.promise.generator.async await 甚至 RxJS ...
- 【JS】285- 拆解 JavaScript 中的异步模式
JavaScript 中有很多种异步编程的方式.callback.promise.generator.async await 甚至 RxJS.我最初接触不同的异步模式时,曾想当然的觉得 promise ...
- ASP.NET MVC EF 中使用异步控制器
最近悟出来一个道理,在这儿分享给大家:学历代表你的过去,能力代表你的现在,学习代表你的将来. 十年河东十年河西,莫欺少年穷 学无止境,精益求精 为什么使用异步操作/线程池 ASP.NET MVC ...
- NodeJS中的异步I/O、事件驱动
nodejs的主要特点是单线程.异步I/O.事件驱动.让我们先大概了解一下这些名词的意思. 单线程 单线程是任务按照顺序执行的,并且每次只执行一个任务,只有前面的任务执行完成以后,后面的任务才执行.在 ...
- C#中的线程一(委托中的异步)
C#中的线程一(委托中的异步) 一.同步委托 我们平时所用的委托以同步居多,我们编写一个方法和相关委托进行演示: publicdelegatevoid DoSomethingDelegate(stri ...
- 看stackoverflow大牛如何回答何时在ASP.NET中使用异步控制器?
转载自博客园:http://farb.cnblogs.com/ 今天无意中看到stackoverflow上一个很好的问答,个人觉得很有价值,所以翻译过来和大家共享!希望大家能相互交流. 在ASP.NE ...
随机推荐
- Leetcode(26)-删除排序数组中的重复项
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度. 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成. 我们利用 ...
- git branch All In One
git branch All In One Git Branch Management https://git-scm.com/book/en/v2/Git-Branching-Branch-Mana ...
- Linux 学习笔记分享: Linux 用户组的权限: drwx------ 700权限(d 目录 ,r=4,w=2,x=1:rwx=7;---=0;---=0)
Linux 用户组的权限: drwx------ 700权限(d 目录 ,r=4,w=2,x=1:rwx=7;---=0;---=0) 1 1 Linux 用户组的权限: drwx------ 700 ...
- Raspberry Pi 电路图模拟器
Raspberry Pi 电路图模拟器 Circuit Diagram / Circuit Graph https://fritzing.org/learning/tutorials/building ...
- Objective C & Swift & iOS & App
Objective C & Swift & iOS & App https://www.runoob.com/ios/ios-objective-c.html https:// ...
- Chrome DevTools & Slow 3G Network
Chrome DevTools & Slow 3G Network shortcuts https://developers.google.com/web/tools/chrome-devto ...
- js assert
js assert console.assert The console.assert() method writes an error message to the console if the a ...
- NGK生态商城即将上线官网,推动生态落地应用
NGK生态商城即将上线官网,以推动生态落地应用.此举意味着NGK生态将跻身区块链顶尖之列,同时,NGK代币.NGK Dapp游戏 "呼叫河马" 以及NGK DeFi项目Baccar ...
- MySQL全面瓦解22:索引的介绍和原理分析
索引的定义 MySQL官方对索引的定义为:索引(Index)是协助MySQL高效获取数据的数据结构. 本质上,索引的目的是为了提高查询效率,通过不断地缩小想要获取数据的范围来筛选出最终想要的结果,同时 ...
- 按键显示器(判断键盘监听器获得的值为普通Key还中modifiers)
1 import sys 2 from PyQt5 import QtWidgets,QtCore 3 from PyQt5.QtCore import Qt 4 from PyQt5.uic.pro ...