Parallel.Foreach c#暂停和停止功能?

时间:2011-12-11 02:07:06

标签: c# parallel-processing parallel.foreach

什么是暂停和停止(在它结束之前)parallel.foreach最有效的方法?

Parallel.ForEach(list, (item) =>
{
    doStuff(item);
});

2 个答案:

答案 0 :(得分:11)

Damien_The_Unbeliver有一个很好的方法,但只有当你想让一些外部进程停止循环时。如果您希望循环中断,就像在普通breakfor循环中使用foreach一样,则需要使用a overload ParallelLoopState作为循环体的参数之一。 ParallelLoopState有两个与您要执行的操作相关的功能Stop()Break()

函数Stop()将在系统最早的方便时停止处理元素 ,这意味着在调用Stop()之后可以执行更多迭代,并且不能保证之前出现的元素你停下来的元素甚至已经开始处理了。

函数Break()的执行与Stop()完全相同,但它也会评估您在IEnumerable上调用的项目之前的Break()的所有元素。当你不关心处理元素的顺序时,这很有用,但是你必须处理所有元素直到你停止的点。

检查从foreach返回的ParallelLoopResult以查看foreach是否提前停止,如果您使用Break(),则处理的编号最小的项目是什么。

Parallel.ForEach(list, (item, loopState) =>
    {
        bool endEarly = doStuff(item);
        if(endEarly)
        {
            loopState.Break();
        }
    }
    );
//Equivalent to the following non parallel version, except that if doStuff ends early
//    it may or may not processed some items in the list after the break.
foreach(var item in list)
{
    bool endEarly = doStuff(item);
    if(endEarly)
    {
        break;
    }
}

这是一个更实用的例子

static bool[] list = new int[]{false, false, true, false, true, false};

long LowestElementTrue()
{
    ParallelLoopResult result = Parallel.ForEach(list, (element, loopState) =>
    {
        if(element)
            loopState.Break();
    }
    if(result.LowestBreakIteration.IsNull)
        return -1;
    else
        return result.LowestBreakIteration.Value;
}   

无论它如何拆分工作,它总会返回2作为答案。

假设处理器调度两个线程来处理它,第一个线程处理元素0-2,第二个线程处理元素3-5。

Thread 1:                Thread 2
0, False, continue next  3, False, continue next
1, False, continue next  4, True, Break
2, True, Break           5, Don't process Broke

现在最低的索引Break从2开始调用,因此ParallelLoopResult.LowestBreakIteration每次都会返回2,无论线程如何被分解,因为它总是会处理到2号。

这是一个如何使用Stop的示例。

static bool[] list = new int[]{false, false, true,  false, true, false};

long FirstElementFoundTrue()
{
    long currentIndex = -1;
    ParallelLoopResult result = Parallel.ForEach(list, (element, loopState, index) =>
    {
        if(element)
        {
             loopState.Stop();

             //index is a 64 bit number, to make it a atomic write
             // on 32 bit machines you must either:
             //   1. Target 64 bit only and not allow 32 bit machines.
             //   2. Cast the number to 32 bit.
             //   3. Use one of the Interlocked methods.
             Interlocked.Exchange (ref currentIndex , index);
        }
    }
    return currentIndex;
}   

根据它分割工作的方式,它将返回2或4作为答案。

假设处理器调度两个线程来处理它,第一个线程处理元素0-2,第二个线程处理元素3-5。

Thread 1:                 Thread 2
0, False, continue next    3, False, continue next
1, False, continue next    4, True, Stop
2, Don't process, Stopped  5, Don't process, Stopped

在这种情况下,它将返回4作为答案。让我们看看相同的过程,但如果它处理所有其他元素而不是0-2和3-5。

Thread 1:                   Thread 2
0, False, continue next     1, False, continue next
2, True, Stop               3, False, continue next
4, Don't process, Stopped   5, Don't process, Stopped

这次它将返回2而不是4。

答案 1 :(得分:2)

为了能够停止Parallel.ForEach,您可以使用其中一个接受ParallelOptions参数的重载,并在这些选项中加入CancellationToken

有关详细信息,请参阅Cancellation

至于暂停,一般来说,我想不出你为什么要这样做。你可能正在寻找一个Barrier(用于协调多个线程之间的工作,比如说他们都需要在继续参加B之前完成A部分),但我不认为你会使用它使用Parallel.ForEach,因为您不知道会有多少参与者。