.net List.ForEach内部Async/Await 异步的问题

发布时间 2023-10-07 10:34:01作者: 水手paul2008

在开发异步的WebApi时,发现一个线程并发错误,起初以为SqlSugar问题。后来,经查是代码写的问题。但此问题,不遇到,不会知晓,只有异步才会出现此问题。

这种问题,也发生在JavaScript下,代码逻辑是一样的。参看这篇:https://advancedweb.hu/how-to-use-async-functions-with-array-foreach-in-javascript/

一般同步模式,ForEach会对每个元素同步执行,上一个执行完成才执行下一个。List.ForEach(x=>{

  Do(x);

});

但是,异步模式不是这样,对每个元素不等待执行完成,会进入下个一元素。如果Do内操作数据库,这会造成多线程相关错误。List.ForEach(async x=>{

  await Do(x);

});

这时,可以用foreach(var x in List){ await Do(x); } 此模式会对List元素一个一个同步执行。

相关代码,我做了个样例,.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 }