2009-09-05 31 views
16

Thread.Sleep(1)を呼び出すこととSwitchToThreadを呼び出すことの間の実際の違いは分かります(現在はBCLによって公開されていないことを無視します)。 "!kernel32のSwitchToThread APIはスリープ(0)とスリープは(1)やるの問題を示さない。"SwitchToThread vs Sleep(1)

ジョーダフィーはというhis postに言及します(スケジューラの動作に関して)

なぜスリープはSwitchToThreadとまったく同じように動作しませんか?なぜこの差別化が存在し、それが何のために良いのでしょうか? (もしあれば)

答えて

21

2つの違いがあります。最初のものはSwitchToThreadのMSDNドキュメントに記載されています。

実行の歩留まりは呼び出しスレッドのプロセッサに制限されています。オペレーティングシステムは、たとえそのプロセッサがアイドル状態であるか、または優先順位の低いスレッドを実行していても、実行を別のプロセッサに切り替えません。

スリープ(0)によって、他のプロセッサ上のスレッドも実行できます。

SwitchToThreadは、単一のスレッドスケジューリングコンテキストにのみ適用されます。一方、睡眠は、それが待つ複数の条件を有する。 SleepExのためのドキュメントは、これを詳細に綴る:

* An I/O completion callback function is called 
* An asynchronous procedure call (APC) is queued to the thread. 
* The time-out interval elapses 

これは、複数のスレッドを生成します。

一般的に、スリープ(0)はタイムスライスを生成する可能性が高く、待機中の他のスレッドがない場合でもOSに常に降伏します。このため、スリープ(0)をループに追加すると、多くの場合プロセッサ使用率が100%(コアあたり)から0%に近くなります。 SwitchToThreadは、他のスレッドがタイムスライスを待っていない限り、実行されません。

+2

あなたの分析が与えられれば、 'if(!SwitchToThread())Sleep(0);'は最良の解決策になりますか? – wilx

0

SwitchToThread()は、スリープ(0)の「スマート」バージョンです。それは十分に文書が、私の理解では、それは以下のように動作されていません:ready状態で、他のスレッドがある

  1. (すなわち複数の論理プロセッサが利用可能なよりも、実行したいスレッドがある)、これらは、スレッドがであり、SwitchToThread()を呼び出すスレッドと同じかそれ以上の優先度を持つであり、Sleep(0)と同じ方法でを実行します。つまり、高価なコンテキストでこれらのスレッドの1つに論理プロセッサを渡します。スイッチ;
  2. ready状態のスレッドがより低い優先度の場合、終了します。つまり、SwitchToThread()を呼び出したスレッドは、コンテキストスイッチや3から0への遷移なしで実行を継続しますユーザーモード) - これはで、Sleep(0)の動作とは逆に、常に優先度の低いスレッドに制御します。;
  3. Slave(0)のように、状態にスレッドが存在しない場合、SwitchToThread()もちょうどとなります。ループでこれを行うと、現在の論理プロセッサの負荷が100%になります。パワーを燃やす。

スリープ(1)は、スリープ(0)と同じですが、遅れて1ミリ秒遅れます。この1ミリ秒の遅延は論理プロセッサを解放し、電力を消費しません。 SwitchToThreadは、逆に、遅延を経験することはありません。

したがって、スリープ(1)はスリープ(0)+ 1ミリ秒の遅延と同じなので、スリープ(1)ではなくスリープ(0)と比較することをお勧めします。

「Intel 64 and IA-32アーキテクチャー最適化リファレンスマニュアル」と「Intel 64 and IA-32アーキテクチャーソフトウェア開発者マニュアル」から、pause CPU命令を呼び出すのが好きなアイディアを借りました。あなたの待機時間が非常に短い場合は、SwitchToThread()またはSleep(0)を使用します。 SwitchToThread()またはSleep(0)はほぼ即時ですが、Sleep(1)は少なくとも1ミリ秒持続します。

次のようにも考慮すべきである:

  • 各呼び出しは、スリープする()またはSwitchToThreadは()10000 +サイクルすることができ、コンテキストスイッチの高価なコストが発生します。
  • また、リング0の遷移のためのリング3のコストは、000 +サイクルである可能性があります。
  • readyステートにスレッドが存在しない場合、SwitchToThread()またはSleep(0)は使用できませんが、他のスレッドが `ready '状態にあるかどうかにかかわらずSleep(1)は少なくとも1ミリ秒待機します。ない。

ウェイトループが非常に短くなる傾向がある場合は、最初にpause CPU命令を実行することを検討してください。より多くのリソースを取得するために待機中のタスクを促進することにより

  • パフォーマンス:SwitchToThread(前のあるpause CPU命令と「スピン・ウェイト」)またはスリープ()呼び出し、マルチスレッドソフトウェアの利益を遅くすることにより忙しい待ち時間から簡単に。
  • スピンしながらパイプラインの少ない部分を使用することによる省電力。
  • SwitchToThread()またはSleep(0)またはSleep(1)呼び出しのオーバーヘッドによって引き起こされる不必要に実行された命令の大半を排除します。

しかし、あなたは睡眠を呼び出すしようとしている場合、(1)少なくとも一つの、あなたのウエイトが非常に長くなることを期待しているよりも、CPUサイクルの面で非常に長いミリ秒なので、pauseを実行しますこの場合、指示は無駄になります。

待ちループが長く続くと予想され、WindowsのOS上などのWaitForSingleObjectなどOSの同期API関数、のいずれかを呼び出すことにより、オペレーティング・システムにもたらすために好ましいが、ないSwitchToThread()またはスリープ(0 )、またはスリープ(1)のいずれかを選択します。さらに、Sleep(1)は非常に遅く、WaitForSingleObjectやEnterCriticalSectionのようなOS同期関数は非常に速く反応し、よりリソースに優しい。

結論:スリープ(0)またはスリープ(1)またはSwitchToThread()を使用しない方が良いです。 "スピンウェイト"ループを回避してください。 WaitForMultipleObjects()、SetEvent()などの高度な同期関数を使用してください。パフォーマンス、効率、省電力という点で最高です。彼らはまた、高価なコンテクストスイッチやリング0の遷移に苦しんでいますが、Sleep()やSwitchToThread()で "スピンウェイト"ループに費やしたコストに比べて、これらの費用はあまりありません。

HTテクノロジをサポートするプロセッサでは、スピンウェイトループはプロセッサの実行帯域幅のかなりの部分を消費する可能性があります。スピンウェイトループを実行する1つの論理プロセッサは、他の論理プロセッサのパフォーマンスに重大な影響を与える可能性があります。そのため、HTを無効にするとパフォーマンスが向上することがあります。

状態変更のためにデバイスやファイルなどのデータソースを一貫してポーリングすると、コンピュータの消費電力が増え、メモリやシステムバスに負荷がかかり、不要なページフォルトが発生する可能性があります。どのアプリケーションがアイドル状態の間にほとんどのページフォルトを生成するかを見るために、Windowsは「ポーリング」を使用しているため、最も非効率的なアプリケーションです)。可能な限りポーリングを最小限に抑え、イベント駆動型のアプリケーション作成方法を使用します。これは私が強く推奨するベストプラクティスです。アプリケーションは文字通り常にスリープ状態になり、事前に設定された複数のイベントを待つ必要があります。イベントドリブンアプリケーションの良い例は、LinuxのNginxです。電源の変更をポーリングする例を挙げてください。オペレーティングシステムが電源をACからバッテリに移行するなど、さまざまなデバイス状態の変更に対して通知サービス(WM_メッセージまで)を提供する場合は、デバイス状態の変更をポーリングする代わりに、これらの通知サービスを使用します。このようなアプローチは、ステータス変更が発生したときにコードが非同期に通知を受け取ることができるため、コードが電源のステータスをポーリングするためのオーバーヘッドを削減します。

一部の人が書いたのとは異なり、スリープ(0)はCPU消費をゼロに近づけません。 「準備完了」状態にある他のスレッドへの実行を解放しますが、そのようなスレッドがない場合は、現在のスレッドのCPUサイクルを無駄にし、CPUサイクルを100%消費します。また、demonstrated by stackoverflow membersもあります。スリープ(0)ループはWindows 10 64ビットで現在のスレッドの100%CPUを消費します。