43

Microsoft 固有の C++ コードを書いているときに、スピンロックよりも書き込みSleep(1)の方がはるかに優れていると言われました。これは、より多くの CPU 時間を使用し、さらに実行を待機している同じ優先度のスレッドがある場合にのみ生成されるためです。Sleep(0)Sleep(0)

std::this_thread::yield()ただし、C++11 スレッド ライブラリでは、 vsの効果に関するドキュメントはあまりありません (少なくとも私が見つけることができたものは) std::this_thread::sleep_for( std::chrono::milliseconds(1) )。2 番目は確かにより詳細ですが、どちらもスピンロックに対して同等に効率的ですか、それともSleep(0)vs.Sleep(1)

std::this_thread::yield()または のいずれかstd::this_thread::sleep_for( std::chrono::milliseconds(1) )が受け入れられるループの例:

void SpinLock( const bool& bSomeCondition )
{
    // Wait for some condition to be satisfied
    while( !bSomeCondition )
    {
         /*Either std::this_thread::yield() or 
           std::this_thread::sleep_for( std::chrono::milliseconds(1) ) 
           is acceptable here.*/
    }

    // Do something!
}
4

4 に答える 4

36

具体的な実装は、基盤となるオペレーティング システムのスケジューリング機能に大きく影響されるため、標準はここではややあいまいです。

そうは言っても、最新の OS ではいくつかのことを安全に想定できます。

  • yield現在のタイムスライスを放棄し、スレッドをスケジューリング キューに再挿入します。スレッドが再度実行されるまでの時間は、通常、完全にスケジューラに依存します。基準では、歩留まりをスケジュール変更の機会と呼んでいることに注意してください。したがって、実装は、必要に応じて、完全に自由に yield からすぐに戻ることができます。利回りはスレッドを非アクティブとしてマークすることは決してないため、利回りでスピンするスレッドは常に 1 つのコアで 100% の負荷を生成します。他のスレッドの準備ができていない場合、再びスケジュールされる前に、現在のタイムスライスの残りを失う可能性があります。
  • sleep_*少なくとも要求された時間、スレッドをブロックします。実装により、 が に変わる場合がありsleep_for(0)ますyield。一方sleep_for(1)、スレッドは中断されます。スケジューリング キューに戻る代わりに、スレッドは最初にスリープ状態のスレッドの別のキューに移動します。要求された時間が経過した後でのみ、スケジューラはスレッドをスケジューリング キューに再挿入することを検討します。短い睡眠によって生じる負荷は依然として非常に高くなります。要求されたスリープ時間がシステム タイムスライスよりも短い場合、スレッドはタイムスライスを 1 つだけスキップすることが予想されます (つまり、アクティブなタイムスライスを解放するための 1 つの譲歩とその後のタイムスライスのスキップ)。 1 つのコアで 100% に近いか、100% に等しいことさえあります。

どちらがスピンロックに適しているかについて一言。スピンロックは、ロックの競合がほとんどまたはまったくないことが予想される場合に最適なツールです。ほとんどの場合、ロックが利用できると予想される場合、スピンロックは安価で価値のあるソリューションです。ただし、競合が発生するとすぐに、スピンロックコストがかかります。ここでyieldとsleepのどちらがより良い解決策であるかについて心配している場合、スピンロックはその仕事にとって間違ったツールです. 代わりにミューテックスを使用する必要があります。

スピンロックの場合、実際にロックを待たなければならない場合は例外と見なす必要があります。したがって、ここで降伏するだけでもまったく問題ありません。これは意図を明確に表現するものであり、そもそも CPU 時間を浪費することは決して問題ではありません。

于 2013-06-26T20:40:11.263 に答える
5

yield の使用中に CPU 負荷に関心がある場合 - 1 つのケースを除いて非常に悪いです (アプリケーションのみが実行されており、基本的にすべてのリソースを消費することを認識しています)。

ここにもっと説明があります:

  • ループでyieldを実行すると、CPUがスレッドの実行を解放することが保証されますが、システムがスレッドに戻ろうとすると、yield操作が繰り返されます。これにより、スレッドが CPU コアの 100% の負荷をフルに使用できるようになります。
  • runningsleep()またはsleep_for()これも間違いです。これによりスレッドの実行がブロックされますが、CPU の待機時間のようなものが発生します。誤解しないでください。これは動作中の CPU ですが、可能な限り低い優先度になっています。単純な使用例 (sleep() で完全にロードされた CPU は、完全にロードされた作業中のプロセッサの半分の悪いことです) のために何とか働いていますが、アプリケーションの責任を確実にしたい場合は、3 番目の例のようなものが必要です:
  • 組み合わせる!:

    std::chrono::milliseconds duration(1);
    while (true)
       {
          if(!mutex.try_lock())
          {
               std::this_thread::yield();
               std::this_thread::sleep_for(duration);
               continue;
          }
          return;
       }
    

このようなものは、この操作が実行されるのと同じくらい速く cpu が生成されることを保証し、さらに sleep_for() は、次の反復を実行しようとする前に cpu がしばらく待機することを保証します。この時間はもちろん、ニーズに合わせて動的 (または静的) に調整できます。

乾杯 :)

于 2014-11-03T19:37:52.993 に答える