.net List.ForEach内部Async/Await 异步的问题
在开发异步的WebApi时,发现一个线程并发错误,起初以为SqlSugar问题。后来,经查是自己写的代码问题。但此问题,不遇到,不会知晓,只有异步才会出现此问题。
这种问题,也发生在JavaScript下,代码逻辑是一样的。参看这篇:https://advancedweb.hu/how-to-use-async-functions-with-array-foreach-in-javascript/
一般同步模式List.ForEach会对每个元素同步执行,上一个执行完成才执行下一个。
List.ForEach(x=>{
Do(x);
});
但是,ForEach内部执行异步方法时逻辑并非想象的那样等待,它不等待异步方法执行完成,就会进入下个一元素。这会造成多线程相关错误。
List.ForEach(async x=>{ await Do(x);//此处不等待 });
这时,可以用foreach等此模式会对List元素一个一个同步执行。
foreach(var x in List) { await Do(x); }
针对此问题,我做了个样例,此样例是.Net 7控制台程序。如下:
1 /*第一种模式 ForEach内部,每个循环,不等待上一步完成,一起执行*/ 2 /* 3 * Expected output: 4 Item:1 Before. Thread:1 5 Item:2 Before. Thread:1 6 Item:3 Before. Thread:1 7 Finish. Thread:1 8 Do:3 Thread:9 9 Item:3 After. Thread:9 10 Do:2 Thread:9 11 Item:2 After. Thread:9 12 Do:1 Thread:9 13 Item:1 After. Thread:9 14 */ 15 //new int[] { 1, 2, 3 }.ToList().ForEach(async x => 16 //{ 17 // Console.WriteLine($"Item:{x} Before. Thread:{Thread.CurrentThread.ManagedThreadId}"); 18 // await A.Do(x); 19 // Console.WriteLine($"Item:{x} After. Thread:{Thread.CurrentThread.ManagedThreadId}"); 20 //}); 21 22 /*第二种模式 foreach内部,每个循环,等待上一步完成,同步执行*/ 23 /* 24 * Expected output: 25 Item:1 Before. Thread:1 26 Do:1 Thread:4 27 Item:1 After. Thread:4 28 Item:2 Before. Thread:4 29 Do:2 Thread:4 30 Item:2 After. Thread:4 31 Item:3 Before. Thread:4 32 Do:3 Thread:4 33 Item:3 After. Thread:4 34 Finish. Thread:4 35 */ 36 foreach (var x in new int[] { 1, 2, 3 }) 37 { 38 Console.WriteLine($"Item:{x} Before. Thread:{Thread.CurrentThread.ManagedThreadId}"); 39 await A.Do(x); 40 Console.WriteLine($"Item:{x} After. Thread:{Thread.CurrentThread.ManagedThreadId}"); 41 42 } 43 44 Console.WriteLine($"Finish. Thread:{Thread.CurrentThread.ManagedThreadId}"); 45 Console.ReadKey(); 46 47 class A 48 { 49 50 public static async Task Do(int a) 51 { 52 await Task.Delay((10 - a) * 1000); 53 Console.WriteLine($"Do:{a} Thread:{Thread.CurrentThread.ManagedThreadId}"); 54 } 55 }
若使用List.ForEach习惯了,可以使用.Net扩展功能,添加如下方法。但我觉得,不如用foreach(var x in list) 直观:为了避免多元素同时异步特意使用foreach。
public static async Task ForEachAsync<T>(this IEnumerable<T> source, Func<T, Task> action) { foreach (var value in source) { await action(value); } }
posted on 2023-10-07 10:27 水手paul2008 阅读(374) 评论(0) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
2015-10-07 html页面缓存问题