我正在尝试使用新的C#4.0 Parallel.ForEach
函数在对象列表上执行并行函数。这是一个非常漫长的维护过程。我想让它按列表的顺序执行,以便我可以在前一点停止并继续执行。我该怎么做呢?
这是一个例子。我有一个对象列表:a1 to a100
。这是当前的订单:
a1, a51, a2, a52, a3, a53...
我想要这个订单:
a1, a2, a3, a4...
我可以将一些对象无序运行,但只要我能在列表中找到一个点,我可以说在此点之前运行了所有对象。我阅读了并行编程csharp白皮书,但没有看到任何相关内容。 ParallelOptions
课程中没有此设置。
答案 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。 “早退出循环”部分涵盖了这种情况。
答案 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
)作为以下问题答案的一部分:
答案 5 :(得分:-2)
由于我的评论似乎不对,因此不确定问题是否已被更改 这里有所改进,基本上提醒并行作业是在你的控制命令之外运行的 打印10个数字可能会产生1,4,6,7,2,3,9,0。
如果您想停止您的计划并稍后继续
在批处理工作负载中,这通常会导致类似的问题
并记录所做的事情。
假如你必须检查10.000数字是否为素数左右
你可以批量循环100,并有一个主要log1,log2,log3
log1 = 0..99
LOG2 = 100..199
请务必设置一些标记以了解批处理作业是否已完成。
这是一个普遍的问题,因为这个问题不是确切的。