C#异步迭代 IAsyncEnumerable 应用
最近用WPF做金税盘开发中有这样一个需求,批量开票每次开票都需要连接一次金税盘。
比如我有发票 a, b ,c ,d e 这五张发票,每次开具发票都需要调用金税盘底层,才能正常开票。
首先,尝试写第一个方法
private void Button_Click(object sender, RoutedEventArgs e)
{
var dateStart = DateTime.Now; //记录用时的起始时间
DebugText = string.Empty;
List<string> fpList = new List<string>() { "a", "b", "c", "d" };
foreach (var item in MockIO)
{
var dateEnd = DateTime.Now;
var timeSpan = dateEnd - dateStart;//记录开票用时
DebugText += item + " " + timeSpan.TotalSeconds + "\r\n";
}
}
/// <summary>
/// 批量开票方法
/// </summary>
/// <param name="ls"></param>
/// <returns></returns>
public static IEnumerable<string> MockIO(List<string> ls)
{
foreach (var item in ls)
{
Task.Delay(1000).Wait();
yield return item;
Debug.WriteLine(Thread.GetCurrentProcessorId());
}
}
来看效果
很明显,发生了UI阻塞情况。因为我们并未对代码做任何异步处理。接下来,我们开始尝试修改。
首先,我们尝试按照常规异步方法修改 MockIO 函数,增加 async 关键词,返回结果增加 Task, 内部对IO操作添加 await。
修改完毕后,编译并没有通过,VS对该方法报异常
通过提示信息,我们可以发现,返回值 Task<IEnumerable<string>> 并不是可以迭代的,因为我们采用了 yield 来返回值,所以我们需要一个可以迭代的返回值。
比如改成这样(非迭代的方式)
调用:
但是,这样一次就返回一组 Task ,没有用到方便的 yield;
此时,就可以用到 IAsyncEnumerable 来设计了,IAsyncEnumerable是C# 8.0引入的新特性,在异步迭代中,非常方便。如上述代码,可以直接修改为
public static async IAsyncEnumerable<string> MockIOAsync(List<string> ls) { foreach (var item in ls) { Task<Task<string>> task = Task<Task<string>>.Factory.StartNew(async () => { await Task.Delay(1000); return item; }); yield return await task.Result; } }
调用(因为采用yield方式,每次返回一个对象都是异步的,所以需要在foreach前加 await)
我们再运行调试,看一下效果
我们可以看到,不仅UI没有被阻塞,同时,传回的值也是一个接一个的传过来的,符合我们的预期。
扩展:虽然上述步骤我们完成的UI的非阻塞的实现,但是我们整个开票用时并没有节省。
接下来,我将继续修改 MockIOAsync 方法,将实现迭代器内部的多线程操作。
修改后的代码如下
public static async Task<IEnumerable<string>> MockIOPerformanceAsync(List<string> ls) { List<string> lss = new List<string>(); List<Task> tasks = new List<Task>(); foreach (var item in ls) { Task task = new Task(() => { Task.Delay(1000).Wait(); Debug.WriteLine(Thread.GetCurrentProcessorId()); lss.Add(item); }); tasks.Add(task); task.Start(); } foreach (var item in tasks) { await item; } return lss; }
效果展示:
嗯,速度很快,但是排序乱了,因为此方法在遍历中新建了线程,list 添加并不保证按照迭代器的顺序添加。有得有失。
源码地址:https://github.com/LuGuangguang/IAsyncEnumerableDemo
翻译 朗读 复制 正在查询,请稍候…… 重试 朗读 复制 复制 朗读 复制 via 百度翻译 译
翻译 朗读 复制 正在查询,请稍候…… 重试 朗读 复制 复制 朗读 复制 via 百度翻译 译
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· SQL Server 2025 AI相关能力初探
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库