调用方法:
const int count = 5; ConsoleExt.WriteLine("Starting Async Streams Demo!"); // 启动一个新任务,用于生成异步数据序列! IAsyncEnumerable<int> pullBasedAsyncSequence = ProduceAsyncSumSeqeunc(count).ToAsyncEnumerable(); ConsoleExt.WriteLineAsync("X#X#X#X#X#X#X#X#X#X# Doing some other work X#X#X#X#X#X#X#X#X#X#"); // 启动另一个新任务,用于消费异步数据序列! var consumingTask = Task.Run(() => ConsumeAsyncSumSeqeunc(pullBasedAsyncSequence)); // 出于演示目的,等待任务完成! consumingTask.Wait(); ConsoleExt.WriteLineAsync("Async Streams Demo Done!");输出:
最后,我们实现了我们想要的行为!我们可以在枚举上进行异步迭代。
源代码在这里。
客户端/服务器端的异步拉取我将使用一个更现实的例子来解释这个概念。客户端/服务器端架构是演示这一功能优势的绝佳方法。
客户端/服务器端同步调用客户端向服务器端发送请求,客户端必须等待(客户端被阻塞),直到服务器端做出响应,如图-3所示。
图-3 同步数据拉取,客户端等待请求完成
异步数据拉取客户端发出数据请求然后继续执行其他操作。一旦有数据到达,客户端就继续处理达到的数据。
图-4 异步数据拉取,客户端可以在请求数据时执行其他操作
异步序列数据拉取客户端发出数据块请求,然后继续执行其他操作。一旦数据块到达,客户端就处理接收到的数据块并询问下一个数据块,依此类推,直到达到最后一个数据块为止。这正是Async Streams想法的来源。图-5显示了客户端可以在收到任何数据时执行其他操作或处理数据块。
图-5 异步序列数据拉取(Async Streams),客户端未被阻塞!
Async Streams与IEnumerable<T>和IEnumerator<T>类似,Async Streams提供了两个新接口IAsyncEnumerable<T>和IAsyncEnumerator<T>,定义如下:
public interface IAsyncEnumerable<out T> { IAsyncEnumerator<T> GetAsyncEnumerator(); } public interface IAsyncEnumerator<out T> : IAsyncDisposable { Task<bool> MoveNextAsync(); T Current { get; } } // Async Streams Feature可以被异步销毁 public interface IAsyncDisposable { Task DiskposeAsync(); }