首页 > 解决方案 > .NET 中的 Parallel.ForEach 和有条件的正常循环

问题描述

以下是否会在 5 处对 500 个项目进行并行处理,但对 1 个项目进行正常循环(没有性能影响)。我不希望在一次执行 1 项时使用 Parallel(如果有的话)对性能造成影响。

 Parallel.ForEach(scheduledMatchups, new ParallelOptions
            {
                MaxDegreeOfParallelism = scheduledMatchups.Count() > 500 ? 5 : 1
            }, 
  gridItem =>
            {
// Process
});

标签: .netmultithreadingperformancetask-parallel-libraryparallel.foreach

解决方案


您可以重构动作和循环序列以具有两个不同的执行路径,同时仍然只定义要执行一次的动作:

Action<GridItems> action = (gridItem) =>
{
    // Process
};

if (scheduledMatchups.Count < 500)
{
    scheduledMatchups.ForEach(action);
    // or for more control, you could iterate yourself
    // with a for or foreach and call the action() manually.
    //
    // foreach(var gridItem in scheduledMatchups) action(gridItem);
}
else
{
    Parallel.ForEach(scheduledMatchups, new ParallelOptions
    {
        MaxDegreeOfParallelism = 5
    }, action);
}

这个关于 SO 的答案提供了一些关于内部发生了什么的见解Parallel.ForEach,如果您最终控制了性能和实现,那么这个简单的分支逻辑仍然只以可重用的方式定义一次操作。

虽然从技术上讲存在开销,但如果您通常期望满足并行条件,那么开销通常不足以证明这种类型的工作只是为了它。

正如@Keith 在下面的评论中指出的那样,Parallel.ForEach它不会,也不是旨在为“迭代次数有限的小循环体”提供性能改进。这在这篇关于并发和并行编程的文章中有所介绍。

如果你的动作足够轻,那么即使有很多次迭代,你最好不要并行处理。


我发现它通常是一个更好的练习,Parallel.ForEach只有当你可以证明你正在执行的动作类型是合理的时候才使用它。如果操作本身需要一些可观察到的时间来处理,那么对于有限数量的操作,开销很可能是合理的。

与所有此类问题一样,您的情况的真正答案将始终取决于比此处实际记录的更多因素,因此您应该尝试两种代码样式,并自行确定任何一种方式的影响是否明显或可接受。

如果您以一种或另一种方式观察问题,请发布有关具体观察的信息。在并行与否之间进行选择时,您应该小心过度过早优化最终使您的代码更难阅读或维护但对实际运行时几乎没有改进的过程。


推荐阅读