AsParallel.ForAll与Parallel.ForEach的区别

44
下面两段代码有什么区别?如果有,是什么? myList.AsParallel().ForAll(i => { /*做某事*/ });Parallel.ForEach(mylist, i => { /*做某事*/ }); 主线程是否会等待所有子线程完成?在MVC应用程序中,如果我在控制器操作中进行并行处理,主线程完成后子线程会发生什么?它们会被中止还是即使在主线程完成后仍将完成?

1
我建议你阅读:http://reedcopsey.com/2010/02/03/parallelism-in-net-part-8-plinqs-forall-method/ - e_ne
4
如果我是你,我会为你的MVC并行处理问题开一个新的提问。 - Pacane
请注意AsParallel().ForAll(),因为它会导致不可预测的结果。例如,当单击按钮时,我有一个执行此代码的按钮:myEnumerable.AsParallel().ForAll(i as string => otherDictionary.Add(i, 0))。它将null作为键添加到otherDictionary中。我不得不重写使用foreach循环。奇怪的事情。 - YukiSakura
2
@YukiSakura,也许你没有使用ConcurrentDictionary?我认为我们不应该因为一个没有完整示例的评论而害怕使用代码。最好将您的问题作为单独的问题发布。 - crokusek
4个回答

24

Parallel.ForEach()旨在用于此类代码。

另一方面,ForAll()旨在在(可能是复杂的)PLINQ查询的末尾使用。

因此,我认为Parallel.ForEach()在这里是更好的选择。

在两种情况下,当前线程将用于执行计算(以及来自线程池的一些线程),并且仅在所有处理完成后方法才会返回。


4
为什么ForAll()应该在PLINQ查询的末尾使用? - Aran Mulholland
2
根据Reed Copsey的说法,被Parallel.Foreach()消耗的并行查询必须为并行化付出两倍的代价。而ForAll()使用查询中现有的分区/线程。 - Kabbalah
1
我猜想由于Parallel.ForEach()来自System.Threading.Tasks命名空间,所以它更可能与任务相关。而AsParallel().ForAll来自System.Linq命名空间,因此更适合与PLINQ一起使用。 - Ben
1
@Ben 我不太明白ForAll是PLINQ的一部分。而且我也不知道"related to tasks"是什么意思。 - svick

4
正如《C#并发编程实战》所述:

Parallel和PLINQ之间的一个区别在于,PLINQ假设它可以使用计算机上的所有核心,而Parallel将动态地响应不断变化的CPU条件。


4

这是MSDN上的解释:

https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/potential-pitfalls-with-plinq#prefer-forall-to-foreach-when-it-is-possible

根据我所读的,如果你想确保顺序访问列表,则使用 Parallel.ForEach() ,而使用 AsParallel.ForAll()则不能保证列表中的项目按顺序访问。

对于MVC,所有线程都是基于请求的。调用者线程(主线程)被阻塞,直到Parallel()调用完成,这意味着所有子线程也应该已经完成。

如果调用线程正在中止,这里有一个解释:

http://www.albahari.com/threading/part5.aspx

PLINQ不会主动中止线程,因为这样做很危险。相反,在取消时,它会等待每个工作线程完成当前元素后结束查询。这意味着查询调用的任何外部方法都将运行到完成。


"根据我所阅读的内容,如果您想确保列表按顺序访问,请使用Parallel.ForEach()。" - 这完全是错误的,不值得被点赞。您完全误解了参考文章。正如名称"parallel"所暗示的,项目并非按顺序枚举。当然,Parallel.ForEach并行方式枚举源集合,即无序地产生结果。 - user21970328
ForAll只适用于PLINQ(Parallel.ForEach不是PLINQ)!如果您没有显式调用ParallelEnumerable.ForAll,那么PLINQ查询实际上是并行枚举的,但是查询结果会使用一个公共的foreach迭代器按顺序合并回来(以保持原始顺序)。 - user21970328
Parallel.ForEach总是以并行方式枚举集合,产生无序的结果。 在发布的链接示例中,尝试通过将PLINQ查询结果(ParallelEnumerable)传递给Parallel.ForEach来将顺序枚举(合并)转换回并行枚举。出于性能原因,你不应该将PLINQ查询传递给Parallel.ForEach,而应该在ParallelEnumerable查询对象上调用ForAllForAll跳过了PLINQ查询的合并部分。 - user21970328

-1

您可以使用AsParallel().AsOrdered()对元素进行排序。在Parallel.ForEach中进行排序不是开箱即用的,我们需要手动实现。


AsOrdered() 影响 结果 的排序。由于 ForAll() 没有任何结果,因此 AsOrdered() 在这里没有任何影响。如果您正在谈论处理的顺序,则仅对单线程计算有意义。 - svick
我是在说你是否使用了ForAll()和AsOrdered()吗?请确认一下。 - thewpfguy
抱歉,我的错误。但是在Parallel.ForEach中使用AsOrdered甚至更没有意义,原始的非并行集合已经有序,因此添加.AsParallel().AsOrdered()不会产生任何效果。此外,尝试对并行计算进行排序很少有意义。 - svick

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接