回答:
通常のロック(ミューテックス、クリティカルセクションなど)を使用する場合、オペレーティングシステムはスレッドをWAIT状態にし、同じコア上の他のスレッドをスケジュールすることによってスレッドをプリエンプトします。スレッドがCPU時間を再び受け取るためにプリエンプションを待機する必要があるため、待機時間が本当に短い場合、これはパフォーマンスを低下させます。
さらに、カーネルオブジェクトは、割り込みハンドラーやページングが利用できない場合など、カーネルのすべての状態で利用できるわけではありません。
スピンロックはプリエンプションを引き起こしませんが、他のコアがロックを解放するまでループ(「スピン」)で待機します。これにより、スレッドがクォンタムを失うのを防ぎ、ロックが解放されるとすぐに続行します。スピンロックの単純なメカニズムにより、カーネルはほとんどすべての状態でそれを利用できます。
そのため、シングルコアマシンのスピンロックは、スレッドのスケジューリングを完全に妨げる「割り込みの無効化」または「IRQLの発生」にすぎません。
スピンロックにより、最終的にカーネルは「ビッグカーネルロック」(コアがカーネルに入り、出口で解放されるときに取得されるロック)を回避し、カーネルプリミティブをきめ細かくロックできるため、マルチコアマシンでのマルチプロセッシングが向上し、パフォーマンスが向上します。
編集:質問が出てきました:「それは、可能な限りスピンロックを使用する必要があるということですか?」そして私はそれに答えようとします:
先に述べたように、スピンロックは、予想される待機時間がクォンタム(読み取り:ミリ秒)よりも短く、プリエンプションがあまり意味がない(たとえば、カーネルオブジェクトが利用できない)場合にのみ役立ちます。
待機時間が不明な場合、またはユーザーモードの場合、スピンロックは効率的ではありません。スピンロックが利用可能かどうかを確認しながら、待機中のコアで100%のCPU時間を消費します。クォンタムが期限切れになるまで、そのコアで他のスレッドが実行されないようにします。このシナリオは、カーネルレベルでの短いバーストでのみ実行可能であり、ユーザーモードアプリケーションのオプションではありそうもありません。
SOについての質問は次のとおりです:スピンロック、それらはどれほど便利ですか?
リソースがロックによって保護されているとしましょう。リソースへのアクセスを希望するスレッドは、最初にロックを取得する必要があります。ロックが使用できない場合、スレッドはロックが解放されたかどうかを繰り返し確認する可能性があります。この間、スレッドはビジー状態で待機し、ロックをチェックし、CPUを使用しますが、有用な作業は行いません。このようなロックは、スピンロックと呼ばれます。
特定の条件が満たされるまでループが続くのは、かなりループです。
while(cantGoOn) {};
sleep(0)
、スレッドがプリエンプトされ、そもそもスピンロックを使用する目的が失われます。他のスレッドに譲る必要がある場合は、通常のロックを使用する必要があります。(私はあなたのコメントが非常に古いことを知っていますが、他の人がこれを提案として見ないようにしたいと思いました)。
それは忙しい待機をするタイプのロックです
これは、非常に低レベルのドライバープログラミング(「適切な」待機関数を呼び出すと、数サイクルの単純なビジーロックよりもオーバーヘッドが大きくなる場合がある)を除いて、アンチパターンと見なされます。
たとえば、Linuxカーネルのスピンロックを参照してください。
リソースがロックされているときにブロックするのではなく、ビジー待機ループに入ってリソースをプールする方が安価であると考えられる場合は、スピンロックを使用することをお勧めします。
スピンは、ロックの粒度が細かく、数が多い場合(リンクリストのノードごとのロックなど)、およびロックの保持時間が常に非常に短い場合に役立ちます。一般に、スピンロックを保持している間は、ブロック、それ自体がブロックする可能性があるものの呼び出し、一度に複数のスピンロックを保持、動的にディスパッチされた呼び出し(インターフェイスと仮想)、静的にディスパッチされた呼び出しを任意のコードに行うことを避ける必要があります所有、またはメモリを割り当てます。
パフォーマンス上の理由から、SpinLockは値の型であることに注意することも重要です。したがって、2つのインスタンス(元のインスタンスとコピー)は互いに完全に独立しているため、誤ってSpinLockインスタンスをコピーしないように十分注意する必要があります。これにより、アプリケーションの誤動作が発生する可能性があります。SpinLockインスタンスを渡す必要がある場合は、値ではなく参照で渡す必要があります。