MaxDegreeOfParallelism==1
的 Parallel.ForEach()
方法是否保证按顺序处理输入的可枚举对象?如果答案是否定的,有没有方法可以强制执行此行为?
MaxDegreeOfParallelism==1
的 Parallel.ForEach()
方法是否保证按顺序处理输入的可枚举对象?Parallel.ForEach方法不能保证执行顺序。与顺序ForEach循环不同,传入的值不总是按顺序处理。
最好按照公共API的设计使用Parallel.ForEach
:以并行方式处理项目。如果需要按顺序处理项目,则最好使用常规的foreach
循环。这比使用MaxDegreeOfParallelism = 1
更清晰。
话虽如此,出于好奇,我查看了.NET 4.7.1的源代码。简而言之,如果MaxDegreeOfParallelism = 1
,则项目将按顺序处理。但是,您不应该依赖此功能进行未来实现,因为可能不总是这样。
查看Parallel.ForEach
并跟随它,最终会看到要迭代的集合被分区(如果是TSource[]
,List<TSource>
或IEnumerable<TSource>
,此过程略有不同)。
ParallelForReplicaTask
中覆盖了Task.SavedStateForNextReplica
和Task.SavedStateFromPreviousReplica
,以便在并行运行的任务之间通信状态。在本例中,它们用于通信任务应迭代哪个分区。
最后,让我们来看一下 Task.ExecuteSelfReplicating
。 ParallelForReplicatingTask
根据指定的并行度以及任务调度程序的 MaximumConcurrencyLevel
来覆盖 ShouldReplicate
。 因此,当 MaxDegreeOfParallelism = 1
时,只会创建一个子任务。因此,此任务仅在创建的单个分区上运行。
所以,回答您的问题:截至目前,MaxDegreeOfParallism = 1
的 Parallel.ForEach
将从头到尾枚举 TSource[]
、从头到尾枚举 IList<TSource>
,以及对于 IEnumerable<TSource>
,使用GetEnumerator
,具体取决于 IEnumerable<TSource>
是否可以转换为 OrderablePartitioner<TSource>
。这三个路径在Parallel.ForEachWorker
中确定。
我强烈鼓励您自己浏览源代码,以便亲眼看到。
我希望这能回答您的问题,但重要的是要记住:不要依赖这个。未来这个实现可能会发生变化。
从MSDN:
Parallel.ForEach方法不保证执行顺序。与顺序ForEach循环不同,传入的值并不总是按顺序处理。
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 示例 在这里
显然,如果你增加 MaxDegreeOfParallelism
,每个并行任务将处理其 块(或 块)的项目 - 按照它们在原始 有序 列表中出现的顺序。
MaxDegreeOfParallelism
为1时做出明确的异常,这样你就可以确保它在所有情况下的一致行为。显然,第一种选择更可取。 - Jeroen MostertParallel.ForEach()
和MaxDegreeOfParallelism==1
有什么意义呢?这似乎只是浪费时间。 - Liam