4

ThreadPool を使用して、ネストされた非同期ループの次の単純な実装を取り上げます。

ThreadPool.SetMaxThreads(10, 10);
CountdownEvent icnt = new CountdownEvent(1);
for (int i = 0; i < 50; i++)
{
    icnt.AddCount();
    ThreadPool.QueueUserWorkItem((inum) =>
    {
        Console.WriteLine("i" + inum + " scheduled...");
        Thread.Sleep(10000);  // simulated i/o
        CountdownEvent jcnt = new CountdownEvent(1);
        for (int j = 0; j < 50; j++)
        {
            jcnt.AddCount();
            ThreadPool.QueueUserWorkItem((jnum) =>
            {
                Console.WriteLine("j" + jnum + " scheduled...");
                Thread.Sleep(20000);  // simulated i/o
                jcnt.Signal();
                Console.WriteLine("j" + jnum + " complete.");
            }, j);
        }
        jcnt.Signal();
        jcnt.Wait();
        icnt.Signal();
        Console.WriteLine("i" + inum + " complete.");
    }, i);
}
icnt.Signal();
icnt.Wait();

現在、このパターンを使用することはありません (開始時にデッドロックが発生します) が、スレッドプールで発生する可能性のある特定のデッドロックを示しています。つまり、ブロックしているスレッドがプール全体を消費した後、ネストされたスレッドが完了するのを待っている間にブロックすることです。

これのネストされた Parallel.For バージョンを使用して、同様に有害な動作を生成する潜在的なリスクがあるかどうか疑問に思っています:

Parallel.For(1, 50, (i) =>
{
    Console.WriteLine("i" + i + " scheduled...");
    Thread.Sleep(10000);  // simulated i/o
    Parallel.For(1, 5, (j) =>
    {
        Thread.Sleep(20000);  // simulated i/o
        Console.WriteLine("j" + j + " complete.");
    });
    Console.WriteLine("i" + i + " complete.");
});

明らかに、スケジューリング メカニズムははるかに洗練されています (そして、このバージョンがデッドロックするのはまったく見たことがありません) が、潜在的なリスクがまだそこに潜んでいる可能性があるようです。Parallel.For が使用するプールを枯渇させて、ネストされたスレッドに依存することでデッドロックを作成することは理論的に可能ですか? つまり、Parallel.For が遅延後にスケジュールされたジョブのバック ポケットに保持するスレッドの数に制限はありますか?

4

1 に答える 1

4

Parallel.For()いいえ、 (または)のようなデッドロックのリスクはありませんParallel.ForEach()

デッドロックのリスクを下げる要因がいくつかあります (使用されるスレッドの動的カウントなど)。しかし、デッドロックが不可能な理由もあります。反復は元のスレッドでも実行されます。つまり、ThreadPoolが完全にビジーの場合、計算は完全に同期して実行されます。その場合、 を使用してもスピードアップは得られませんParallel.For()が、コードは引き続き実行され、デッドロックは発生しません。

また、 s を使用した同様の状況Taskも正しく解決されます。まだスケジュールされていない で (またはその にアクセス) すると、現在のスレッドでインラインで実行されWait()ますTaskResultこれは主にパフォーマンスの最適化だと思いますが、特定のケースでデッドロックを回避することもできると思います。

しかし、問題は実際的というよりも理論的なものだと思います。.Net 4ThreadPoolでは、デフォルトの最大スレッド数が 1,000 程度に設定されています。Threadそして、同時に何千ものブロックがある場合、あなたは何か非常に間違ったことをしています.

于 2012-06-30T00:42:01.590 に答える