异步编程已经流行很多年了,.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. 牛客多校第九场H Cutting Bamboos(主席树 区间比k小的个数)题解

    题意: 标记为\(1-n\)的竹子,\(q\)个询问,每次给出\(l,r,x,y\).要求为砍区间\(l,r\)的柱子,要求砍\(y\)次把所有竹子砍完,每次砍的时候选一个高度,把比他高的都砍下来,并 ...

  2. 使用 js 实现十大排序算法: 冒泡排序

    使用 js 实现十大排序算法: 冒泡排序 冒泡排序 refs xgqfrms 2012-2020 www.cnblogs.com 发布文章使用:只允许注册用户才可以访问!

  3. UIKit and SwiftUI

    UIKit and SwiftUI Live Preview Try Again or Resume refs xgqfrms 2012-2020 www.cnblogs.com 发布文章使用:只允许 ...

  4. color recognition by image

    color recognition by image 通过图像进行颜色识别 https://imagecolorpicker.com/ unknown color origin pic grey bl ...

  5. how to auto open a url in the browser by using terminal

    how to auto open a url in the browser by using terminal Linux / MacOS # bash / zsh $ open http://loc ...

  6. SameSite cookies explained

    SameSite cookies explained

  7. BGV等 DeFi产品暴涨背后隐藏着什么?

    比特币突破两万七千美金,在此创造了历史.在比特币一路飙升的背后,到底是谁注入了"强心针".笔者认为今年以来推动BTC长期上涨的主要动力主要包括四个:经济形势恶化.央行大量放水(主要 ...

  8. Egg.js 是什么?

    Egg.js 是什么? 阿里巴巴出 Egg.js 为企业级框架和应用而生,我们希望由 Egg.js 孕育出更多上层框架,帮助开发团队和开发人员降低开发和维护成本. 注:Egg.js 缩写为 Egg 设 ...

  9. JUnit5学习之四:按条件执行

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  10. Mybites学习

    参考链接:https://www.cnblogs.com/dongying/p/4073259.html <select <!-- 1. id (必须配置) id是命名空间中的唯一标识符, ...