为啥要先执行 Return 语句而不是 Parallel。 ForEach [重复]
Posted
技术标签:
【中文标题】为啥要先执行 Return 语句而不是 Parallel。 ForEach [重复]【英文标题】:Why execute the Return statement first instead of the Parallel. ForEach [duplicate]为什么要先执行 Return 语句而不是 Parallel。 ForEach [重复] 【发布时间】:2020-01-18 01:15:48 【问题描述】:我想使用 Parallel.ForEach 来操作一组并返回另一组。但是我似乎得到了一个空的。并且有一个异步方法需要在Parallel.ForEach中执行。
这是 Windows 10 中带有 netcore2.2 的控制台应用程序。
public static ConcurrentBag<int> GetList()
ConcurrentBag<int> result = new ConcurrentBag<int>() ;
List<int> list = new List<int> 1, 2, 3 ;
Parallel.ForEach(list, async i =>
await Task.Delay(i*1000);
result.Add(i * 2);
);
return result;
public static void Main(String[] args)
List<int> list = new List<int>();
var res = GetList();
list.AddRange(res);
Console.WriteLine("Begging.");
foreach (var item in list)
Console.WriteLine(item);
Console.ReadLine();
我希望是 2,4,6,但实际上是一个空的。
【问题讨论】:
你为什么使用async
和await Task.Delay(i*1000);
? Parallel.ForEach
不能很好地与 async
配合使用。
您可能想要考虑做的是return list.AsParallel(stuffhere).ToList();
。 我的意思是,这毫无意义,但它会给你并行性。
不要将Parallel.ForEach
用于异步代码。它仅用于数据并行性,不能等待任何任务。您所做的是启动一堆甚至在您的应用程序终止之前可能不会运行的任务。 Parallel.ForEach 没有方法可以等待这些任务
ConcurrentBag 也不是一个好的选择。与其他并发集合不同,它是一个 specialized 类,它使用线程本地存储来允许更快地访问创建对象的线程。这意味着与使用 ConcurrentQueue 相比,主线程将花费更长的时间来读取结果
【参考方案1】:
await
是罪魁祸首。您需要了解的是await
是一个花哨的回报。如果调用者不理解任务,它所看到的只是返回。 Parallel.ForEach
不期望返回任务的委托,因此它不知道如何等待 await
完成。
Parallel.ForEach
几乎一开始就结束了,早在任何东西被写入result
之前。
现在,Parallel
应该用于 CPU 密集型操作,所以这不是问题。如果要模拟长时间运行的 CPU 密集型操作,请使用 Thread.Sleep
而不是 await Task.Delay
。
附带说明一下,您将如何并行化受 I/O 限制的基于任务的操作?最简单的方法是这样的:
await Task.WhenAll(list.Select(YourAsyncOperation));
其中YourAsyncOperation
是一个返回Task
的异步方法,它可以尽可能多地使用Task.Delay
。这种简单方法的主要问题是您必须确保YourAsyncOperation
实际上很快就会执行await
,并且理想情况下不使用同步上下文。在最坏的情况下,所有调用都将被序列化。好吧,真的,在最坏的情况下,你会陷入僵局,但是...... :)
【讨论】:
以上是关于为啥要先执行 Return 语句而不是 Parallel。 ForEach [重复]的主要内容,如果未能解决你的问题,请参考以下文章
为啥 main 的默认返回值为 0 而不是 EXIT_SUCCESS?
Java finally语句到底是在return之前还是之后执行?