3

以下のようなコードがあります。

long progress = 0;

using (Timer timer = new Timer(state => { Console.Write(Interlocked.Read(ref progress); }, null, 5000, 5000)
{
  Parallel.ForEach(list, item =>
  {
     item.DoTask();
     Interlocked.Incrememt(ref progress);
  }
}

コンソールに書き込まれる数値が散発的に書き込まれるため、スレッド プールの枯渇が発生しているようです (確かに 5 秒ごとではありません)。3 つの数字が立て続けに書き込まれる前に、15 秒間の長い一時停止がある場合があります。タイマーが Parallel.ForEach と戦ってスレッドプールからスレッドを取得し、コールバックをスケジュールしていると思います。

どうすればこれを回避できますか?

4

1 に答える 1

2

スレッドプールが不足しているようです

飢餓はおそらく間違ったメンタルモデルです。スレッドプール スケジューラの仕事は、実行中の tp スレッドの数を、ThreadPool.SetMinThreads() によって設定された最小値に抑えることです。デフォルトは、マシンで使用できるプロセッサ コアの数と同じです。より多くの同時実行を許可することは、通常は有害です。オペレーティング システムのスレッド スケジューラは、時間がかかるそれらの間のコンテキスト切り替えを強制されます。

これらの tp スレッドが実際にコアを燃やす場合、これは考慮事項です。彼らが「データベース処理」を行うとき、これはあなたの状況ではほとんどありません。これには通常、dbase エンジンが操作を実行するのを待機するスレッドである、多くのデッド タイムが含まれます。

Timer コールバックもスレッドプールと競合し、そのコールバックは tp スレッドで実行されます。tp スケジューラは、既存のスレッドが 0.5 秒以内に完了しない場合、別のスレッドを実行できるようにします。

正味の効果は、あなたが説明するものです。プログラムで多くの CPU が使用されていることはありません。Parallel.ForEach() がそれを達成しようとしているため、「クランプ」は明確な可能性があります。より多くのtp スレッドを同時に実行できるようにしたいので、少なくともいくつかは意味のある作業を行うことができ、他のスレッドは dbase エンジンを待っています。

実際にそれが得られるかどうかはかなり疑問ですが、実際にプログラムを調整しているのは dbase エンジンである可能性が高いです。次にネットワーク帯域幅です。次は、サーバーに過負荷をかけるのが嫌いな dbase 管理者です。ThreadPool.SetMinThreads() をいじることはできますが、奇跡を期待しないでください。

また、タイマー コールバックの再入を防ぐ必要があることに注意してください。既存の Parallel.ForEach() ループがまだ完了していないときにタイマーが作動するのはかなり悪いことです。

于 2013-07-02T16:49:38.940 に答える