异步编程已经流行很多年了,.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> 解析的更多相关文章

  1. 一文说通C#中的异步迭代器

    今天来写写C#中的异步迭代器 - 机制.概念和一些好用的特性   迭代器的概念 迭代器的概念在C#中出现的比较早,很多人可能已经比较熟悉了. 通常迭代器会用在一些特定的场景中. 举个例子:有一个for ...

  2. 深入Asyncio(八)异步迭代器

    Async Iterators: async for 除了async def和await语法外,还有一些其它的语法,本章学习异步版的for循环与迭代器,不难理解,普通迭代器是通过__iter__和__ ...

  3. [译]Python中的异步IO:一个完整的演练

    原文:Async IO in Python: A Complete Walkthrough 原文作者: Brad Solomon 原文发布时间:2019年1月16日 翻译:Tacey Wong 翻译时 ...

  4. 【JS】336- 拆解 JavaScript 中的异步模式

    点击上方"前端自习课"关注,学习起来~ JavaScript 中有很多种异步编程的方式.callback.promise.generator.async await 甚至 RxJS ...

  5. 【JS】285- 拆解 JavaScript 中的异步模式

    JavaScript 中有很多种异步编程的方式.callback.promise.generator.async await 甚至 RxJS.我最初接触不同的异步模式时,曾想当然的觉得 promise ...

  6. ASP.NET MVC EF 中使用异步控制器

    最近悟出来一个道理,在这儿分享给大家:学历代表你的过去,能力代表你的现在,学习代表你的将来. 十年河东十年河西,莫欺少年穷 学无止境,精益求精   为什么使用异步操作/线程池 ASP.NET MVC ...

  7. NodeJS中的异步I/O、事件驱动

    nodejs的主要特点是单线程.异步I/O.事件驱动.让我们先大概了解一下这些名词的意思. 单线程 单线程是任务按照顺序执行的,并且每次只执行一个任务,只有前面的任务执行完成以后,后面的任务才执行.在 ...

  8. C#中的线程一(委托中的异步)

    C#中的线程一(委托中的异步) 一.同步委托 我们平时所用的委托以同步居多,我们编写一个方法和相关委托进行演示: publicdelegatevoid DoSomethingDelegate(stri ...

  9. 看stackoverflow大牛如何回答何时在ASP.NET中使用异步控制器?

    转载自博客园:http://farb.cnblogs.com/ 今天无意中看到stackoverflow上一个很好的问答,个人觉得很有价值,所以翻译过来和大家共享!希望大家能相互交流. 在ASP.NE ...

随机推荐

  1. sdut3562-求字典序最小的最短路 按顶点排序后spfa的反例

    首先我们可以这么搞...倒序建图,算出源点s附近的点距离终点的距离,然后判断一下,终点是否能跑到源点 能跑到的话呢,我们就判断s周围的点是否在最短路上,然后我们选编号最小的点就好了 代码 #inclu ...

  2. 多线程(一)java并发编程基础知识

    线程的应用 如何应用多线程 在 Java 中,有多种方式来实现多线程.继承 Thread 类.实现 Runnable 接口.使用 ExecutorService.Callable.Future 实现带 ...

  3. 高并发之Phaser、ReadWriteLock、StampedLock

    本系列研究总结高并发下的几种同步锁的使用以及之间的区别,分别是:ReentrantLock.CountDownLatch.CyclicBarrier.Phaser.ReadWriteLock.Stam ...

  4. js 十大排序算法 All In One

    js 十大排序算法 All In One 快速排序 归并排序 选择排序 插入排序 冒泡排序 希尔排序 桶排序 堆排序(二叉树排序) 基数排序 计数排序 堆排序(二叉树排序) https://www.c ...

  5. Node.js Backend Developer

    Node.js Backend Developer refs xgqfrms 2012-2020 www.cnblogs.com 发布文章使用:只允许注册用户才可以访问!

  6. OAuth 2.0 All In One

    OAuth 2.0 All In One 授权类型 授权代码 隐式 密码凭证 客户端凭证 授权码 授权码授予类型要求用户向提供者进行身份验证-然后将授权码发送回客户端应用程序,提取并与提供者交换以获取 ...

  7. js 构造函数 & 静态方法 & 原型 & 实例方法

    js 构造函数 & 静态方法 & 原型 & 实例方法 ES5 "use strict"; /** * * @author xgqfrms * @licens ...

  8. WebAssembly in Action

    WebAssembly in Action 数据加密,反爬虫,防盗链,版权保护,数据追踪,埋点 blogs 加密,js 禁用检测,权限控制 WebAssembly 防盗链 wasm online id ...

  9. node mailer & email bot

    node mailer & email bot email https://nodemailer.com/about/ https://github.com/nodemailer/nodema ...

  10. TypeScript 3.7 RC & Optional Chaining

    TypeScript 3.7 RC & Optional Chaining https://devblogs.microsoft.com/typescript/announcing-types ...