异步编程曾经风行很多年了,.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/art...
更多高质量干货:参见我的 GitHub: csharptranslate