0

コンソールアプリで集中的な作業を行う約5000人のバックグラウンドワーカーを作成します。ObjectXなどのオブジェクトをインスタンス化する外部ライブラリも使用しています。ある時点、たとえばt0で、ObjectXはosスレッドプールからスレッドを取得して開始しようとしますが、このスレッドを取得する方法を制御することはできません。100人のバックグラウンドワーカーにとっては問題なく機能します。1000人のバックグラウンドワーカーの場合、ObjectXがスレッドを取得して開始するのにt0から約10分かかります。

  1. オブジェクトによって将来開始されるスレッドに高い優先度を事前に設定する方法はありますか?

  2. 1の答えは「いいえ」だと思いますが、バックグラウンドワーカーの優先順位を制限して、他のすべてを何らかの形で優先する方法はありますか?ObjectXを「支持」したいだけなのに。

目標は、マシンがどれほど過負荷であっても、ObjectXによって起動されたスレッドを実行するために常に利用可能なリソースを用意することです。

Windows 64ビットマシンでC#と.Netfr3.5を使用しています。

4

3 に答える 3

3

スレッドが機能する方法は、OS によってプロセッサ時間が与えられることです。これが起こるとき、これはコンテキストスイッチと呼ばれます。コンテキストの切り替えには、約 2000 ~ 8000 サイクルかかります (つまり、プロセッサの 2000 ~ 8000 命令によって異なります)。OS に多数の CPU またはコアがある場合、CPU を 1 つのスレッドから切り離して別のスレッドに渡す必要がない場合があります。これにより、コンテキストの切り替えが回避されます。一度に実行できる CPU ごとのスレッドは 1 つだけです。CPU よりも CPU を必要とするスレッドが多い場合は、コンテキスト スイッチを強制します。コンテキストの切り替えは、システム クォンタムより速く実行されません (クライアントの場合は 20 ミリ秒ごと、サーバーの場合は 120 ミリ秒ごと)。

5000 のバックグラウンド ワーカーがある場合、事実上 5000 のスレッドがあります。これらの各スレッドは、潜在的に CPU 時間を争っています。Windows のクライアント バージョンでは、1 秒あたり 250,000 回のコンテキスト スイッチを意味します。つまり、1 秒あたり 500,000,000 から 2,000,000,000 サイクルが、単にスレッド間の切り替えに費やされます。(つまり、スレッドが実行している作業に加えて) 1 秒あたりにその数のコンテキスト スイッチを処理できる場合

推奨される方法は、プロセッサごとに 1 つの CPU バウンド スレッドのみを持つことです。CPU バウンド スレッドは、「待機」にほとんど時間を費やさないスレッドです。UI スレッドは、CPU バウンド スレッドではありません。バックグラウンド ワーカーがロックの待機に多くの時間を費やしている場合、それらも CPU バウンドではない可能性がありますが、一般に、バックグラウンド ワーカー スレッドは CPU バウンドです。(そうでなければ、バックグラウンド ワーカーを使用する意味は何でしょう?)。

また、OS は、次に CPU を取得するためにどのスレッドが必要かを判断するのに多くの時間を費やします。スレッドの優先度を変更し始めると、それが妨げられ、ほとんどの場合、システム全体 (アプリケーションだけでなく) が速くなるどころか遅くなります。

アップデート:

関連する問題では、新しいスレッドを作成するのに約 200,000 サイクル、スレッドを破棄するのに約 100,000 サイクルかかります。

更新 2:

質問の原動力が単に「できるかどうか」ではなく、ワークロードをスケーリングできるようにすることである場合、@ JoshW / @Servyが言及しているように、プロデューサー/コンシューマーパターンのようなものを使用すると、水平方向を促進できるスケーラビリティが可能になりますキューまたはサービス バスを介して複数のコンピューター/ノードにスケーリングします。適切な量​​のスレッドを単純に起動しても、CPU の数を超えて拡張することはできません。「利用可能なリソース...マシンがどれだけ過負荷になっているか」という理由でスケールアウトできるアーキテクチャが本当に必要な場合は、単に不可能です。

于 2013-03-12T15:11:34.610 に答える
0

ただし、個人的にはこれは悪い考えだと思います...他の回答についてのコメントと、「ObjectXができるだけ早く実行されるバックグラウンドワーカーがいくつ作成されても」というリクエストを考えると...おそらく強制することができますManualResetEvent を使用してブロックするバックグラウンド ワーカー。

たとえば、ワーカー コードの先頭で、WaitOne メソッドを使用して手動リセット イベントをブロックできます。この手動リセットは、静的にすることも、入力パラメーターとして渡すこともできます。ObjectX がインスタンス化/呼び出される場所などで、ManualResetEvent の .Reset メソッドを呼び出します。これにより、WaitOne 行ですべてのワーカーがブロックされます。次に、ObjectX を実行するコードの最後で、ManualResetEvent.Set() メソッドを呼び出します。これにより、ワーカーのブロックが解除されます。

これはスレッドを管理する効率的な方法ではないことに注意してください。ただし、「機能させるだけ」で、後で改善する時間があれば... 1つの可能な解決策だと思います。

于 2013-03-12T15:26:07.853 に答える
-1

目標は、マシンがどれほど過負荷になっても、ObjectX によって起動されたスレッドを実行するためのリソースを常に利用できるようにすることです。

その場合、スレッドの優先度は適切なツールではない可能性があります.スレッドの優先度は悪であることを忘れないでください

一般に、Windows はリアルタイム OS ではありません。特に、win32 はソフト リアルタイムになろうともしません (IIRC、NT カーネルは、ある時点で、少なくともソフト リアルタイム サブシステムをサポートしようとしましたが、私は間違っているかもしれません)。そのため、利用可能なリソースやタイミングに関する保証はありません。

また、システム内の他のスレッドについて心配していますか? これらのスレッドは制御できません (他のスレッドがすでにシステムの最大優先度にある場合はどうなるでしょうか?)。アプリのスレッドが心配な場合は...より少ないスレッド/ワーカーを使用してより多くの作業を行うことで、それらを制御および調整できます (たとえば、より大きな単位で作業をバッチ処理し、それをワーカーに送信するか、TPL を使用するか、スレッドの使用を処理および調整する他のツール)

そうは言っても、スレッドが作成されたときにインターセプトすることができます (たとえば、この質問https://stackoverflow.com/a/3802316/863564を見てください) 。それを後押しします。

于 2013-03-12T15:01:56.937 に答える