平行的.Foreach c#暂停和停止函数

本文关键字:函数 暂停 Foreach | 更新日期: 2023-09-27 18:17:23

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

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

平行的.Foreach c#暂停和停止函数

damien_the_unbelieve有一个很好的方法,但这只是当你想让一些外部进程停止循环。如果你想让循环像在正常的forforeach循环中使用break一样爆发,你将需要使用一个具有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。

<>之前线程1:线程20、假,下一个继续。3、假,下一个继续1、假,继续下一个4、真,中断2、正确,打破5、不要处理打破之前

现在被调用的最低索引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:线程20、假,下一个继续。3、假,下一个继续1、假,下一步继续。4、真,停止2、不加工,停止之前

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

<>之前线程1:线程20,假,下一个继续1,假,下一个继续2、为真,停止。3、为假,下一步继续4、不加工,停止之前

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

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

详情请参见取消。

至于暂停,我不明白你为什么要这样做。您可能正在寻找一个Barrier(用于协调多个线程之间的努力,例如如果它们都需要在继续进行B部分之前完成a部分),但我不认为您会使用Parallel.ForEach,因为您不知道将有多少参与者。