await foreach与异步流IAsyncEnumerable简介学习如何使用 await foreach 遍历异步流1.什么是异步流异步流(Async Streams)是C#8.0引入的特性用于异步地产生和消费数据序列。为什么需要异步流?传统方式需要等待所有数据准备好才能返回。异步流可以逐个产生数据不需要等待全部完成。适合处理大量数据或需要异步获取的数据。应用场景从数据库逐行读取数据。从网络流式读取数据。处理大文件逐块读取。实时数据流处理。2.IAsyncEnumerableT接口定义IAsyncEnumerableT异步可枚举接口代表能够异步遍历的集合。C#public interface IAsyncEnumerableout T{//获取异步迭代器支持传入取消令牌IAsyncEnumeratorT GetAsyncEnumerator(CancellationToken cancellationToken default);}特点方法返回值类型支持IAsyncEnumerableT。方法内部使用yield return逐个产出元素。方法体内可以使用await执行异步操作。示例C#//返回异步可枚举集合支持异步逐个产出数据async IAsyncEnumerableint GetNumbersAsync(){// 循环生成0~9数字for (int i 0; i 10; i){// 异步等待100毫秒模拟IO异步操作await Task.Delay(100);// 异步迭代产出当前数值iyield return i;}}C#//使用 await foreach 遍历异步流await foreach (var num in GetNumbersAsync()){Console.WriteLine(num);}3.await foreach语法基本用法await foreach专门用来遍历异步流IAsyncEnumerableT。C#//遍历异步可枚举对象await foreach (var item in asyncEnumerable){Console.WriteLine(item); // 处理每一个异步产出的数据}特点只能标记在async修饰的方法内部使用。每一轮迭代都会异步等待下一个元素准备好。完成等待过程不阻塞线程线程可去执行其他任务。完整示例C#async Task Main(){//使用 await foreach 消费异步流await foreach (var num in GetNumbersAsync()){Console.WriteLine(num);}}4.异步迭代器方法创建异步迭代器使用async和yield return创建异步迭代器。C#//异步逐行读取文件返回异步数据流async IAsyncEnumerablestringReadLinesAsync(string path){//创建文件读取流using自动释放资源using var reader new StreamReader(path);string? line;// 异步读取一行文本读到末尾返回null时结束循环while ((line await reader.ReadLineAsync()) ! null){//流式向外返回读取到的一行文本yield return line;}}说明方法返回类型必须是IAsyncEnumerableT。使用yield return逐个产出数据。方法内部允许使用await执行异步操作。string不可空字符串理论上不能赋值nullstring?可空字符串变量允许存放string实例 或者null。5.完整示例逐行读取文件定义异步流方法C#/// summary///异步逐行读取文件返回异步数据流/// /summaryasync IAsyncEnumerablestring ReadLinesAsync(string path){using var reader new StreamReader(path);// string?可空字符串接收ReadLineAsync可能返回的nullstring? line;// 异步读取一行读到文件末尾返回null时退出循环while ((line await reader.ReadLineAsync()) ! null){//流式返回当前读取到的一行文本yield return line;}}使用 await foreach 消费异步流C#//程序入口支持async Task MainC#7.1async Task Main(){// await foreach 遍历异步流逐行获取文件内容await foreach (string line in ReadLinesAsync(file.txt)){Console.WriteLine(line); // 处理每一行数据}}要点总结async IAsyncEnumerableTyield return 异步迭代器。方法内部可以正常使用await执行 IO 异步操作。调用端必须使用await foreach遍历不能用普通foreach。流式处理不会一次性加载整个文件到内存适合超大文件读取。string? line适配ReadLineAsync()文件末尾返回null的行为消除可空引用类型编译警告执行流程。调用ReadLinesAsync→ 进入await foreach迭代 → 触发读取一行 →yield return推送该行 → 外部执行打印 → 继续下一行读取直到文件结尾。6.对比传统方式需要等待全部数据C#//返回TaskListstring一次性加载所有行async TaskListstring ReadAllLinesAsync(string path){// 创建集合用来存放所有读取到的文本行var lines new Liststring();// 创建文件读取器执行完毕自动释放资源using var reader new StreamReader(path);// string? 可空字符串接收可能返回null的读取结果string? line;//循环异步读取每一行while ((line await reader.ReadLineAsync()) ! null){lines.Add(line); //将读取到的行加入集合}return lines;//弊端必须**把文件全部读取完成之后**才能返回整个集合}7.异步流方式逐个产生数据C#//返回异步流 IAsyncEnumerablestringasync IAsyncEnumerablestring ReadLinesAsync(string path){using var reader new StreamReader(path);string? line;while ((line await reader.ReadLineAsync()) ! null){yield return line;//优势读到一行立刻向外输出一行不需要等待全部读完}}两者核心区别传统方式 TaskListT先把所有数据读到内存 List 中调用方必须等待全部数据加载完成才能开始处理文件很大时占用大量内存容易内存压力高。异步流 IAsyncEnumerableT流式处理读到一行、推送一行调用方通过await foreach拿到一行就能立刻处理同一时刻内存只保存单行数据适合超大文件、流式数据场景。7.异步流的应用场景1.文件处理逐行读取大文件。逐块读取文件。实时处理文件内容。2.数据库查询逐行读取查询结果。处理大量数据。减少内存占用。3.网络流流式下载文件。实时接收数据。处理 WebSocket 消息。4.实时数据传感器数据流。日志流处理。事件流处理。8.异步流的优势内存效率响应性可拓展性简洁性。