【问题标题】:Chunk partitioning IEnumerable in Parallel.ForeachParallel.Foreach 中的块分区 IEnumerable
【发布时间】:2013-05-07 19:41:57
【问题描述】:

有谁知道让 Parallel.Foreach 循环使用块分区的方法,我认为默认情况下是范围分区。使用数组时看起来很简单,因为您只需创建一个自定义分区器并将负载平衡设置为 true。

由于 IEnumerable 中的元素数量直到运行时才知道,我似乎无法找到让块分区工作的好方法。

任何帮助将不胜感激。

谢谢!

我尝试在每个对象上执行的任务需要花费显着不同的时间来执行。最后,我通常要等待几个小时才能完成最后一个线程的工作。我想要实现的是沿途有并行循环请求块,而不是为每个线程预先分配项目。

【问题讨论】:

  • 你为什么要这个(出于兴趣)
  • 你是按块的数量还是按每个块的大小来分块的?
  • 这取决于 IEnumerable 的来源以及来自数据源的 yield return 循环中发生的情况。如果具体对象已经(几乎)是一个数组,则根本不执行任何工作。
  • 嗯,消耗IEnumerable 确实需要时间。但是,如果您知道无论哪种方式都可以完成,它甚至可以提高性能。 IEnumerable 的整个想法是“不必消耗所有东西,因为你不需要全部使用”。正如 Scott 提到的,如果 IEnumerable 已经包装了一个数组或一个列表,那么根本就没有性能问题。
  • 顺便问一下,元素的顺序有问题吗?如果没有,您可以使用它们的 index % chunksCount 作为“块 id”(即使使用 Linq)将元素“推送”到不同的 IEnumerable 中。

标签: c# .net multithreading parallel-processing


【解决方案1】:

如果您的 IEnumerable 确实具有索引器(即您可以执行 obj[1] 来获取项目),您可以执行以下操作

    var rangePartitioner = Partitioner.Create(0, source.Length);
    Parallel.ForEach(rangePartitioner, (range, loopState) =>
    {
        // Loop over each range element without a delegate invocation. 
        for (int i = range.Item1; i < range.Item2; i++)
        {
            var item = source[i]
            //Do work on item
        }
    });

但是,如果它不能做到这一点,您必须通过创建从System.Collections.Concurrent.Partitioner&lt;TSource&gt; 派生的新类来编写自定义分区器。该主题过于广泛,无法在 SO 答案中涵盖,但您可以查看 this guide on the MSDN 以帮助您入门。

更新:从 .NET 4.5 开始,他们添加了一个不缓冲数据的 Partitioner.Create 重载,它与创建一个范围最大大小为 1 的自定义分区器具有相同的效果。有了这个如果不幸地连续处理一堆慢项目,你将不会得到一个有一堆排队工作的单个线程。

var partitoner = Partitioner.Create(source, EnumerablePartitionerOptions.NoBuffering);
Parallel.ForEach(partitoner, item =>
{
    //Do work
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-04-16
    • 2014-04-11
    • 1970-01-01
    • 1970-01-01
    • 2021-08-21
    • 1970-01-01
    • 1970-01-01
    • 2013-02-13
    相关资源
    最近更新 更多