スピンロックとセマフォ

スピンロックとセマフォは、主に次の 4 つの点で異なります。

<強い>1.概要
スピンロック ロックの可能な実装の 1 つです。つまり、ビジー待機 (「スピニング」) によって実装されます。セマフォはロックの一般化です (または、逆に、ロックはセマフォの特殊なケースです)。通常、必ずしもそうとは限りません 、スピンロックは 1 つのプロセス内でのみ有効ですが、セマフォは異なるプロセス間の同期にも使用できます。

ロックは相互排除のために機能します。つまり、1 です。 スレッドは一度にロックを取得し、コードの「クリティカル セクション」に進むことができます。通常、これは、複数のスレッドで共有される一部のデータを変更するコードを意味します。
セマフォ カウンターがあり、1 つまたは複数に取得される可能性があります スレッドに投稿する値に応じて、(一部の実装では) 最大許容値に応じて。

その限りでは、ロックは最大値が 1 のセマフォの特殊なケースと見なすことができます。

<強い>2.役割
前述のように、スピンロックはロックであるため、相互排除 (厳密には 1 対 1) メカニズムです。通常はアトミックな方法で、メモリの場所を繰り返しクエリおよび/または変更することによって機能します。つまり、スピンロックの取得は「ビジー」操作であり、CPU サイクルを長時間 (おそらく永遠に) 消費する可能性がありますが、実際には「何も」達成されません。
このようなアプローチの主なインセンティブは、コンテキスト スイッチのオーバーヘッドが数百回 (または場合によっては 1000 回) のスピンに相当するという事実です。もっと効率的。また、リアルタイム アプリケーションの場合、ブロックして、スケジューラが将来的に戻ってくるのを待つことは受け入れられない場合があります。

対照的に、セマフォはまったくスピンしないか、非常に短い時間だけスピンします (システムコールのオーバーヘッドを回避するための最適化として)。セマフォを取得できない場合、セマフォはブロックされ、実行準備ができている別のスレッドに CPU 時間が割り当てられます。これはもちろん、スレッドが再びスケジュールされる前に数ミリ秒が経過することを意味する場合がありますが、これが問題にならない場合 (通常はそうではありません)、非常に効率的で CPU を節約するアプローチになる可能性があります。

<強い>3.輻輳時の動作
スピンロックまたはロックフリー アルゴリズムが「一般に高速」である、または「非常に短いタスク」にのみ役立つというのはよくある誤解です (理想的には、絶対に必要な時間よりも長く同期オブジェクトを保持するべきではありません)。
1 つの重要な違いは、さまざまなアプローチが 輻輳の存在下でどのように動作するかです。 .

適切に設計されたシステムは、通常、輻輳が少ないか、まったくありません (これは、すべてのスレッドがまったく同時にロックを取得しようとするわけではないことを意味します)。たとえば、通常はしない ロックを取得し、ネットワークから 0.5 メガバイトの zip 圧縮データをロードし、データをデコードして解析し、最後にロックを解放する前に共有参照を変更する (コンテナーにデータを追加するなど) コードを記述します。代わりに、共有リソースにアクセスする目的でのみロックを取得します。 .
これは、クリティカル セクションの内側よりも外側にかなり多くの作業があることを意味するため、スレッドがクリティカル セクションの内側にある可能性は比較的低く、同時にロックをめぐって競合するスレッドはほとんどありません。もちろん、ときどき 2 つのスレッドが同時にロックを取得しようとします (これができなかった場合)。 たまたまロックは必要ないでしょう!)、しかしこれは「健全な」システムのルールというよりむしろ例外です。

そのような場合、スピンロックは大いに ロックの輻輳がない場合、スピンロックを取得するオーバーヘッドは、コンテキスト スイッチの数百/数千サイクルまたはタイム スライスの残りを失うための 1000 万から 2000 万サイクルと比較して、わずか数十サイクルであるため、セマフォよりも優れています。

一方、輻輳が激しい場合、またはロックが長時間保持されている場合 (それを仕方がない場合もあります!)、スピンロックは何も達成しないために非常に多くの CPU サイクルを消費します。
この場合、セマフォ (またはミューテックス) を使用することをお勧めします。別のスレッドを便利実行できるからです。 その間のタスク。または、他のスレッドが実行するのに役立つものがない場合、オペレーティング システムが CPU を抑制し、熱を減らしてエネルギーを節約できるようにします。

また、シングルコア システムでは、ロックの輻輳が存在する場合、スピンロックは非常に非効率的です。これは、回転しているスレッドが、発生する可能性のない状態変更を待機するために完全な時間を浪費するためです (解放スレッドがスケジュールされるまでではなく、起こっていない 待機中のスレッドが実行されている間!)。したがって、与えられた 任意の 競合の量、ロックを取得するには、最良のケースで約 1 と 1/2 のタイム スライスが必要です (リリース スレッドがスケジュールされている次のスレッドであると仮定します)。これはあまり良い動作ではありません。

<強い>4.実装方法
現在、セマフォは通常 sys_futex をラップします。 Linux で (オプションで、数回の試行後に終了するスピンロックを使用)。
スピンロックは通常、アトミック操作を使用して実装され、オペレーティング システムによって提供されるものは何も使用しません。以前は、これはコンパイラ組み込み関数または移植性のないアセンブラ命令のいずれかを使用することを意味していました。一方、C++11 と C11 の両方には、言語の一部としてアトミック操作が含まれているため、ロックフリー コードを証明可能で正確に記述することの一般的な難しさは別として、ロックフリー コードを完全に移植可能な (ほぼ) コードで実装できるようになりました。


簡単に言えば、セマフォは「譲歩する」同期オブジェクトであり、スピンロックは「ビジーウェイト」オブジェクトです。 (単一のスレッドからコード領域を保護するミューテックス、ガード、モニター、またはクリティカル セクションとは異なり、セマフォは複数のスレッドを同期するという点でもう少しあります)

より多くの状況でセマフォを使用しますが、ロックする時間が非常に短い場合はスピンロックを使用します。特にロックを頻繁に行う場合は、ロックにコストがかかります。このような場合、保護されたリソースがロック解除されるのを待っている間、スピンロックする方が効率的です。回転が長すぎると明らかにパフォーマンスが低下します。

通常、スレッド クォンタムよりも長くスピンする場合は、セマフォを使用する必要があります。


Yoav Aviram と gbjbaanb が言ったことに加えて、もう 1 つの重要なポイントは、シングル CPU マシンでは決してスピンロックを使用しないということでしたが、セマフォはそのようなマシンでは意味があります。最近では、複数のコアやハイパースレッディング、または同等の機能を備えていないマシンを見つけるのに苦労することがよくありますが、CPU が 1 つしかない状況では、セマフォを使用する必要があります。 (理由は明らかだと思います。1 つの CPU が他の何かがスピンロックを解放するのを待ってビジーであるが、それが唯一の CPU で実行されている場合、現在のプロセスまたはスレッドがこれにはしばらく時間がかかる場合があり、プリエンプションが発生するまで何も役に立ちません。)