6

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

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

2 回答 2

12

Damien_The_Unbeliver有一个很好的方法,但前提是你想让一些外部进程停止循环。如果您想让循环像break在普通forforeach循环中使用 a 一样中断,您将需要使用具有 a作为循环体参数之一的重载。有两个与您想要做的事情相关的功能,和.ParallelLoopStateParallelLoopStateStop()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。

线程 1:线程 2
0,假,下一个继续 3,假,下一个继续
1,假,继续下一步 4,真,中断
2、True、Break 5、不处理 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。

线程 1:线程 2
0,假,下一个继续 3,假,下一个继续
1,假,继续下一步 4,真,停止
2,不处理,停止 5,不处理,停止

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

线程 1:线程 2
0,假,下一个继续 1,假,下一个继续
2,真,停止 3,假,下一个继续
4,不处理,停止 5,不处理,停止

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

于 2011-12-11T03:17:16.897 回答
2

为了能够停止 a Parallel.ForEach,您可以使用接受ParallelOptions参数的重载之一,并CancellationToken在这些选项中包含 a 。

有关更多详细信息,请参阅取消

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

于 2011-12-11T02:12:52.480 回答