スレッドプールが不足しているようです
飢餓はおそらく間違ったメンタルモデルです。スレッドプール スケジューラの仕事は、実行中の tp スレッドの数を、ThreadPool.SetMinThreads() によって設定された最小値に抑えることです。デフォルトは、マシンで使用できるプロセッサ コアの数と同じです。より多くの同時実行を許可することは、通常は有害です。オペレーティング システムのスレッド スケジューラは、時間がかかるそれらの間のコンテキスト切り替えを強制されます。
これらの tp スレッドが実際にコアを燃やす場合、これは考慮事項です。彼らが「データベース処理」を行うとき、これはあなたの状況ではほとんどありません。これには通常、dbase エンジンが操作を実行するのを待機するスレッドである、多くのデッド タイムが含まれます。
Timer コールバックもスレッドプールと競合し、そのコールバックは tp スレッドで実行されます。tp スケジューラは、既存のスレッドが 0.5 秒以内に完了しない場合、別のスレッドを実行できるようにします。
正味の効果は、あなたが説明するものです。プログラムで多くの CPU が使用されていることはありません。Parallel.ForEach() がそれを達成しようとしているため、「クランプ」は明確な可能性があります。より多くのtp スレッドを同時に実行できるようにしたいので、少なくともいくつかは意味のある作業を行うことができ、他のスレッドは dbase エンジンを待っています。
実際にそれが得られるかどうかはかなり疑問ですが、実際にプログラムを調整しているのは dbase エンジンである可能性が高いです。次にネットワーク帯域幅です。次は、サーバーに過負荷をかけるのが嫌いな dbase 管理者です。ThreadPool.SetMinThreads() をいじることはできますが、奇跡を期待しないでください。
また、タイマー コールバックの再入を防ぐ必要があることに注意してください。既存の Parallel.ForEach() ループがまだ完了していないときにタイマーが作動するのはかなり悪いことです。