当使用Parallel.Foreach()
使用4个线程的100个项目时,它会将列表分成4个块(0-24,25-49,50-74,75-99)个项目,这意味着,该项目0,25,50和75是并行处理的。
是否有可能以模数方式对项目进行分区以首先处理索引较低的项目?像:
Thread 1: 0, 5, 9,..
Thread 2: 1, 6, 10,...
Thread 3: 2, 7, 11,...
Thread 4: 3, 8, 12,...
答案 0 :(得分:1)
这种分区方法称为Round Robin或Striping。与Parallel.ForEach()
一起使用的主要挑战是ForEach()
要求分区器支持动态分区,这种类型的分区是不可能的,因为在执行循环之前必须修复分区数。
实现此类分区的一种方法是创建从System.Collections.Concurrent.Partitioner<TSource>
派生的自定义类,并使用ParallelQuery.ForAll()
方法,该方法没有动态分区支持要求。对于大多数应用程序,这应该等同于使用ForEach()
。
以下是自定义Partitioner
的示例和基本实现。 Partitioner
将生成与并行度相同数量的分区。
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
namespace RoundRobinPartitioning
{
public class RoundRobinPartitioner<TSource> : Partitioner<TSource>
{
private readonly IList<TSource> _source;
public RoundRobinPartitioner(IList<TSource> source)
{
_source = source;
}
public override bool SupportsDynamicPartitions { get { return false; } }
public override IList<IEnumerator<TSource>> GetPartitions(int partitionCount)
{
var enumerators = new List<IEnumerator<TSource>>(partitionCount);
for (int i = 0; i < partitionCount; i++)
{
enumerators.Add(GetEnumerator(i, partitionCount));
}
return enumerators;
}
private IEnumerator<TSource> GetEnumerator(
int partition,
int partitionCount)
{
int position = partition;
TSource value;
while (position < _source.Count)
{
value = _source[position];
position += partitionCount;
yield return value;
}
}
}
class Program
{
static void Main(string[] args)
{
var values = Enumerable.Range(0, 100).ToList();
var partitioner = new RoundRobinPartitioner<int>(values);
partitioner.AsParallel()
.WithDegreeOfParallelism(4)
.ForAll(value =>
{
// Perform work here
});
}
}
}