6

parallel.foreach を一時停止して (終了する前に) 停止する最も効果的な方法は何でしょうか?

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

2 に答える 2

12

Damien_The_Unbeliverには優れた方法がありますが、それは外部プロセスにループを停止させたい場合に限られます。break通常forまたはループで aを使用するように、ループをブレークアウトさせたい場合は、ループ本体のパラメーターの 1 つとしてa を持つオーバーロードforeachを使用する必要があります。には、目的に関連する 2 つの機能があり、とがあります。ParallelLoopStateParallelLoopStateStop()Break()

この関数は、システムの最も早いタイミングでStop()要素の処理を停止します。つまり、Stop() を呼び出した後にさらに反復を実行でき、停止した要素の前にある要素が処理を開始したことさえ保証されません。

この関数Break()は とまったく同じように実行されますが、呼び出したアイテムの前にあるStop()のすべての要素も評価されます。これは、要素がどの順序で処理されるかは気にしないが、停止した時点までのすべての要素を処理する必要がある場合に役立ちます。IEnumerableBreak()

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 が返されます。

プロセッサがこれを処理するために 2 つのスレッドをディスパッチするとします。最初のスレッドは要素 0 ~ 2 を処理し、2 番目のスレッドは要素 3 ~ 5 を処理します。

スレッド 1: スレッド 2
0、False、次に続行 3、False、次に続行
1、False、次へ続く 4、True、Break
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 が返されます。

プロセッサがこれを処理するために 2 つのスレッドをディスパッチするとします。最初のスレッドは要素 0 ~ 2 を処理し、2 番目のスレッドは要素 3 ~ 5 を処理します。

スレッド 1: スレッド 2
0、False、次に続行 3、False、次に続行
1、False、次に進む 4、True、停止
2、処理しない、停止 5、処理しない、停止

この場合、答えとして 4 が返されます。同じプロセスを見てみましょう。ただし、0 ~ 2 および 3 ~ 5 ではなく、1 つおきの要素を処理する場合です。

スレッド 1: スレッド 2
0、False、次に続行 1、False、次に続行
2、True、停止 3、False、次に進む
4、処理しない、停止 5、処理しない、停止

今回は 4 ではなく 2 を返します。

于 2011-12-11T03:17:16.897 に答える
2

a を停止できるようにするには、パラメーターParallel.ForEachを受け入れるオーバーロードの 1 つを使用し、それらのオプションに a を含めます。ParallelOptionsCancellationToken

詳しくはキャンセルについてをご覧ください。

一時停止については、一般的に、なぜあなたがそれをしたいのかわかりません。バリア(複数のスレッド間の作業を調整するために使用されます。たとえば、パート B に進む前にパート A を完了する必要がある場合) を探しているかもしれませんが、それを で使用するとは思わないでしょうParallel.ForEach。参加者が何人になるかわかりません。

于 2011-12-11T02:12:52.480 に答える