Parallel.Foreach与模数分区

时间:2017-06-07 15:02:11

标签: c# parallel.foreach

当使用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,...

1 个答案:

答案 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
                });
        }
    }
}