Parallel.ForEach Ordered Execution

时间:2010-09-03 21:46:17

标签: c#-4.0 foreach parallel-processing html-lists

我正在尝试使用新的C#4.0 Parallel.ForEach函数在对象列表上执行并行函数。这是一个非常漫长的维护过程。我想让它按列表的顺序执行,以便我可以在前一点停止并继续执行。我该怎么做呢?

这是一个例子。我有一个对象列表:a1 to a100。这是当前的订单:

a1, a51, a2, a52, a3, a53...

我想要这个订单:

a1, a2, a3, a4...

我可以将一些对象无序运行,但只要我能在列表中找到一个点,我可以说在此点之前运行了所有对象。我阅读了并行编程csharp白皮书,但没有看到任何相关内容。 ParallelOptions课程中没有此设置。

6 个答案:

答案 0 :(得分:3)

如果您使用Parallel.Break来终止循环,那么您将被保证所有低于返回值的索引都将被执行。这几乎就是你能得到的。此处的示例使用For但ForEach具有类似的重载。

int n = ...
var result = new double[n];

var loopResult = Parallel.For(0, n, (i, loopState) =>
{
   if (/* break condition is true */)
   {
      loopState.Break();
      return;
   }
   result[i] = DoWork(i);
});

if (!loopResult.IsCompleted && 
        loopResult.LowestBreakIteration.HasValue)
{
   Console.WriteLine("Loop encountered a break at {0}", 
                      loopResult.LowestBreakIteration.Value);
}

在ForEach循环中,为每个分区中的每个元素在内部生成迭代索引。执行不按顺序执行,但在中断后您知道所有低于LowestBreakIteration的迭代都将完成。

取自“使用Microsoft .NET进行并行编程”http://parallelpatterns.codeplex.com/

在MSDN上可用。见http://msdn.microsoft.com/en-us/library/ff963552.aspx。 “早退出循环”部分涵盖了这种情况。

另请参阅:http://msdn.microsoft.com/en-us/library/dd460721.aspx

答案 1 :(得分:2)

做这样的事情:

int current = 0;
object lockCurrent = new object();

Parallel.For(0, list.Count, 
             new ParallelOptions { MaxDegreeOfParallelism = MaxThreads },
             (ii, loopState) => {
                    // So the way Parallel.For works is that it chunks the task list up with each thread getting a chunk to work on...
                    // e.g. [1-1,000], [1,001- 2,000], [2,001-3,000] etc...
                    // We have prioritized our job queue such that more important tasks come first. So we don't want the task list to be
                    // broken up, we want the task list to be run in roughly the same order we started with. So we ignore tha past in 
                    // loop variable and just increment our own counter.
                    int thisCurrent = 0;
                    lock (lockCurrent) {
                        thisCurrent = current;
                        current++;
                    }
                    dothework(list[thisCurrent]);
                 });

你可以看到当你打破并行for循环时,你将知道要执行的最后一个列表项,假设你让所有线程在破坏之前完成。我不是PLINQ或LINQ的忠实粉丝。老实说,我不知道编写LINQ / PLINQ如何导致可维护的源代码或可读性...... Parallel.For是一个更好的解决方案。

答案 2 :(得分:1)

作为备用建议,您可以记录已运行的对象,然后在恢复执行时过滤列表以排除已运行的对象。

如果需要在应用程序重新启动时保持持久性,则可以存储已执行对象的ID(我假设这些对象具有一些唯一标识符)。

答案 3 :(得分:1)

对于遇到此问题的任何其他人 - 如果您循环遍历数组或列表(而不是IEnumberable),则可以使用Parallel.Foreach的重载,该重载使元素索引也能保持原始顺序。 / p>

string[] MyArray; // array of stuff to do parallel tasks on 
string[] ProcessedArray = new string[MyArray.Length];
Parallel.ForEach(MyArray, (ArrayItem,loopstate,ArrayElementIndex) =>
{
    string ProcessedArrayItem = TaskToDo(ArrayItem);
    ProcessedArray[ArrayElementIndex] = ProcessedArrayItem;
});

答案 4 :(得分:1)

对于任何寻找简单解决方案的人,我发布了2个扩展方法(一个使用PLINQ,另一个使用Parallel.ForEach)作为以下问题答案的一部分:

Ordered PLINQ ForAll

答案 5 :(得分:-2)

由于我的评论似乎不对,因此不确定问题是否已被更改 这里有所改进,基本上提醒并行作业是在你的控制命令之外运行的 打印10个数字可能会产生1,4,6,7,2,3,9,0。

如果您想停止您的计划并稍后继续 在批处理工作负载中,这通常会导致类似的问题 并记录所做的事情。
假如你必须检查10.000数字是否为素数左右 你可以批量循环100,并有一个主要log1,log2,log3
log1 = 0..99
LOG2 = 100..199
请务必设置一些标记以了解批处理作业是否已完成。

这是一个普遍的问题,因为这个问题不是确切的。