如何优化众所周知的耗时过程的工作队列

时间:2016-12-17 11:18:58

标签: c# task-parallel-library

我有一个IEnumerable动作,它们在执行时消耗的时间是排序的。现在我希望所有这些都能并行执行。有没有比这个更好的解决方案?

IEnumerable<WorkItem> workItemsOrderedByTime = myFactory.WorkItems.DecendentOrderedBy(t => t.ExecutionTime);
Parallel.ForEach(workItemsOrderedByTime, t => t.Execute(), Environment.ProcessorCount);

所以我的想法是首先根据他们需要完成的时间执行所有费用任务。

编辑:问题是,是否有更好的解决方案可以在最短的时间内完成所有工作。

2 个答案:

答案 0 :(得分:6)

解决您的XY Problem

  

因为否则可能会发生10个任务中的9个已完成,最后一个任务在1个核心上执行,所有其他核心无效。

您需要做的是告诉Parallel.ForEach一次只从源列表中获取一个项目。这样,当你完成最后一项时,你不会在一个核心的队列中拥有一堆缓慢的工作项。

可以使用Partitioner.Create并传递EnumerablePartitionerOptions.NoBuffering

来完成此操作
Parallel.ForEach(Partitioner.Create(workItems, EnumerablePartitionerOptions.NoBuffering), 
                new ParallelOptions{MaxDegreeOfParallelism = Environment.ProcessorCount},
                t => t.Execute());

答案 1 :(得分:2)

  1. 默认情况下,Parallel.ForEach
  2. 中没有执行订单保证
  3. 这就是为什么你对DecendentOrderedBy的电话没有做任何好事的原因。虽然它可能会做坏事:如果默认分区程序决定执行范围分区,将12 WorkItems分为4组3个项目,按IEnumerable中的顺序排序。然后,第一个核心还有很多工作要做,从而产生了你试图避免的问题。
  4. Scott在答案中解释了对(2)的简单修复。如果Parallel.ForEach仅占用一个项目,那么您自然会获得一些负载平衡。在大多数情况下,这将正常工作
  5. 有序IEnumerable的最佳(大多数情况下)解决方案(如您所见)将为Striped Partitioning个桶数=内核数。 AFIK那里你没有在.NET中获得这种开箱即用的功能。但是,您可以提供自定义OrderablePartitioner,以这种方式对数据进行分区。
  6. 我很遗憾地说:“没有免费的午餐”