3

My Parallel.ForEachループでは、localFinallyデリゲートがすべてのスレッドで呼び出されます。パラレルループが停止すると、これが発生することがわかりました。私の並列ループには、ループが完了する前に戻る約3つの条件チェックステージがあります。そして、localFinallyデリゲートを実行しないのは、スレッドがこれらのステージから返され、本体全体の実行ではない場合のようです。

ループ構造は次のとおりです。

 var startingThread = Thread.CurrentThread;
 Parallel.ForEach(fullList, opt,
         ()=> new MultipleValues(),
         (item, loopState, index, loop) =>
         {
            if (cond 1)
                return loop;
            if (cond 2)
                {
                process(item);
                return loop;
                }
            if (cond 3)
                return loop;

            Do Work(item);
            return loop;
          },
          partial =>
           {
              Log State of startingThread and threads
            } );

小さなデータセットでループを実行し、詳細にログに記録したところ、Parallel.ForEachがすべての反復を完了し、localFinallyの最後のスレッドのログが次のようになっていることがわかりまし
た。ループはまだ正常に完了せず、ストールしたままです...ストールする理由の手がかりはありますか?

乾杯!

4

2 に答える 2

1

localFinally(各スレッドが終了した後に実行される)の定義を確認した後、簡単なテストを実行しました。これは、実行されるループよりも並列処理によって作成されるスレッドがはるかに少ないことを意味するのではないかと思いました。例えば

        var test = new List<List<string>> ();
        for (int i = 0; i < 1000; i++)
        {
            test.Add(null);
        }

        int finalcount = 0;
        int itemcount = 0;
        int loopcount = 0;

        Parallel.ForEach(test, () => new List<string>(),
            (item, loopState, index, loop) =>
            {
                Interlocked.Increment(ref loopcount);
                loop.Add("a");
                //Thread.Sleep(100);
                return loop;
            },
            l =>
            {
                Interlocked.Add(ref itemcount, l.Count);                    
                Interlocked.Increment(ref finalcount);                    
            });

このループの終わりには、itemcountとloopcountは予想どおり1000であり、(私のマシンでは)実行速度に応じてfinalcountは1または2でした。条件のある状況では、直接返されると、実行はおそらくはるかに高速であり、追加のスレッドは必要ありません。作業が実行された場合にのみ、より多くのスレッドが必要になります。ただし、パラメーター(私の場合はl)には、すべての実行の組み合わせリストが含まれています。これがロギングの違いの原因である可能性がありますか?

于 2012-06-07T12:14:31.397 に答える
1

localFinallyあなたはどういう意味か誤解していると思います。アイテムごとに呼び出されるのではなく、によって使用されるスレッドごとに呼び出されParallel.ForEach()ます。また、多くのアイテムが同じスレッドを共有できます。

それが存在する理由は、各スレッドで独立していくつかの集約を実行し、最終的にのみそれらを結合できるためです。このように、非常に小さなコードでのみ同期を処理する必要があります(そしてそれがパフォーマンスに影響を与えるようにします)。

たとえば、アイテムのコレクションのスコアの合計を計算する場合は、次のように実行できます。

int totalSum = 0;
Parallel.ForEach(
    collection, item => Interlocked.Add(ref totalSum, ComputeScore(item)));

しかし、ここではInterlocked.Add()、すべてのアイテムを要求しますが、これは遅くなる可能性があります。とを使用するlocalInitlocalFinally、次のようにコードを書き直すことができます。

int totalSum = 0;
Parallel.ForEach(
    collection,
    () => 0,
    (item, state, localSum) => localSum + ComputeScore(item),
    localSum => Interlocked.Add(ref totalSum, localSum));

コードはInterlocked.Add()でのみ使用しlocalFinally、のグローバル状態にアクセスすることに注意してくださいbody。このように、同期のコストは、使用されるスレッドごとに1回ずつ、数回だけ支払われます。

注:Interlockedこの例では、非常に単純で明らかに正しいため、使用しました。コードがもっと複​​雑な場合は、lock最初に使用し、Interlockedパフォーマンスを向上させるために必要な場合にのみ使用するようにします。

于 2012-06-07T14:40:34.233 に答える