MaxDegreeOfParallelism=1 时 Parallel.ForEach 会按顺序处理吗?

Will Parallel.ForEach process in order with MaxDegreeOfParallelism=1?

Parallel.ForEach()MaxDegreeOfParallelism==1 是否保证按顺序处理输入可枚举?

如果答案是 "no",有没有办法强制执行此行为?

来自 MSDN:

The Parallel.ForEach method does not guarantee the order of execution. Unlike a sequential ForEach loop, the incoming values aren't always processed in order.

https://msdn.microsoft.com/library/ff963552.aspx

首先,Microsoft's official documentation on parallel programming执行顺序不保证是正确的

The Parallel.ForEach method does not guarantee the order of execution. Unlike a sequential ForEach loop, the incoming values aren't always processed in order.

最好使用 Parallel.ForEach,因为 public API 设计为:以并行方式处理项目。如果您需要按顺序处理项目,最好使用常规 foreach 循环。意图比使用 MaxDegreeOfParallelism = 1.

更清楚

话虽如此,出于好奇,我查看了 .NET 4.7.1 的源代码。简短的回答是 是的,如果 MaxDegreeOfParallelism = 1,项目将按顺序处理。但是,您不应该在未来的实施中依赖它,因为它可能并不总是这样。

  1. 看一下 Parallel.ForEach 并继续执行,您最终会看到要迭代的集合已分区(此过程略有不同,无论它是 TSource[]List<TSource>IEnumerable<TSource>.

  2. Task.SavedStateForNextReplicaTask.SavedStateFromPreviousReplicaParallelForReplicaTask 中被覆盖,以便在任务 运行 之间并行通信状态。在这种情况下,它们用于传达任务应该遍历哪个分区。

  3. 最后来看一下Task.ExecuteSelfReplicatingParallelForReplicatingTask 根据指定的并行度以及任务调度程序的 MaximumConcurrencyLevel 覆盖 ShouldReplicate。因此,使用 MaxDegreeOfParallelism = 1 只会创建一个子任务。因此,此任务将仅对创建的单个分区进行操作。

因此,回答您的问题:在撰写本文时,Parallel.ForEachMaxDegreeOfParallism = 1 将枚举集合 from beginning to end for a TSource[], from beginning to end for an IList<TSource>, and use GetEnumerator for an IEnumerable<TSource>, with slightly different paths depending on if the IEnumerable<TSource> can be cast to an OrderablePartitioner<TSource> or not. These three paths are determined in Parallel.ForEachWorker.

我强烈建议您自己浏览源代码以亲自查看。

我希望这能够回答你的问题,但记住这一点非常重要:不要依赖这个。这种实现很可能在未来发生变化。

答案是否定的,但您可以使用 AsOrdered() 强制执行此行为,如下所示。

using System;
using System.Linq;
using System.Threading.Tasks;

var items = Enumerable.Range(0, 1000).ToArray();
Parallel.ForEach(items.AsParallel().AsOrdered(),
    new ParallelOptions { MaxDegreeOfParallelism = 1 },
    Console.WriteLine);

SharpLab 样本here

显然,如果您增加 MaxDegreeOfParallelism,每个并行任务将处理其 chunk(或 chunks)项 - 在它们在原始 ordered 列表中出现的顺序。