セマフォとスピンロックの基本的な違いは何ですか?
いつスピンロックでセマフォを使用しますか?
スピンロックとセマフォは、主に4つの点で異なります。
1。それらは何ですか
A spinlockは、ロックの可能な実装の1つです。つまり、ビジー待機(「スピニング」)によって実装されます。セマフォはロックの一般化です(または、逆に、ロックはセマフォの特殊なケースです)。通常、ではないが、スピンロックは1つのプロセス内でのみ有効ですが、セマフォは異なるプロセス間で同期するためにも使用できます。
ロックは相互排他に対して機能します。つまり、oneスレッドは一度にロックを取得し、コードの「クリティカルセクション」に進むことができます。通常、これは、複数のスレッドで共有される一部のデータを変更するコードを意味します。
A セマフォにはカウンターがあり、自分が投稿する値に応じて1つまたは複数スレッドによる取得を許可します(一部の実装では)その最大許容値に応じて。
これまでのところ、ロックは最大値が1のセマフォの特殊なケースと考えることができます。
2。彼らがすること
上記のように、スピンロックはロックであるため、相互排除(厳密には1対1)メカニズムです。通常はアトミックな方法で、メモリロケーションを繰り返しクエリおよび/または変更することで機能します。これは、スピンロックを取得することは「ビジー」操作であり、CPUサイクルを長時間(おそらく永遠に)燃焼させる一方で、「何もない」ことを効果的に達成することを意味します。
このようなアプローチの主なインセンティブは、コンテキストスイッチのオーバーヘッドが数百(または数千)回の回転に相当するため、数サイクルの回転を焼き付けてロックを取得できる場合は、全体的に非常に効率的です。また、リアルタイムアプリケーションの場合、スケジューラが将来遠く離れた場所に戻ってくるのをブロックして待つことは受け入れられない場合があります。
対照的に、セマフォはまったくスピンしないか、または非常に短い時間だけスピンします(syscallのオーバーヘッドを回避するための最適化として)。セマフォを取得できない場合、セマフォはブロックされ、実行する準備ができている別のスレッドにCPU時間を割り当てます。これはもちろん、スレッドが再びスケジュールされるまでに数ミリ秒かかることを意味するかもしれませんが、これが問題にならない場合(通常はそうではありません)、それは非常に効率的なCPU保守的なアプローチになります。
。輻輳が発生した場合の動作
スピンロックまたはロックフリーアルゴリズムは「一般に高速」である、または「非常に短いタスク」にのみ有用であるという一般的な誤解です(理想的には、同期オブジェクトを絶対に必要以上に長く保持しないでください。これまで)。
1つの重要な違いは、さまざまなアプローチの動作です輻輳がある場合。
適切に設計されたシステムでは、通常、輻輳が少ないか、まったくありません(これは、すべてのスレッドがまったく同時にロックを取得しようとしないことを意味します)。たとえば、通常はnotロックを取得してネットワークからZip圧縮データの半メガバイトを読み込み、データをデコードおよび解析し、最後に共有参照を変更する(データを追加する)コードを記述しますロックを解除する前に、コンテナなどに)。代わりに、共有リソースにアクセスする目的でのみロックを取得します。
これは、クリティカルセクションの内側よりも外側の方がかなり多くの作業があることを意味するため、当然、スレッドがクリティカルセクションの内側にある可能性は比較的低く、したがって、同時にロックのために競合するスレッドはほとんどありません。もちろん、時々2つのスレッドが同時にロックを取得しようとします(これがcould n'tの場合、ロックは必要ありません!)、これはむしろ例外です「健全な」システムのルール。
このような場合、スピンロックgreatlyは、ロックの輻輳がない場合、スピンロックを取得するオーバーヘッドがコンテキストスイッチの数百/数千サイクルに比べて数十サイクルであるため、セマフォよりも優れています。タイムスライスの残りを失うための1000〜2000万サイクル。
一方、混雑が激しい場合、またはロックが長時間保持されている場合(場合によってはどうしても助けられない場合があります!)、スピンロックは何も達成しないために非常に多くのCPUサイクルを消費します。
セマフォ(またはミューテックス)は、この場合、別のスレッドでsefulタスクを実行できるため、はるかに優れた選択肢です。または、他に便利なものがないスレッドがある場合は、オペレーティングシステムがCPUを抑制し、熱を減らしてエネルギーを節約できます。
また、シングルコアシステムでは、スピンロックはロックの輻輳があると非常に非効率になります。スピンするスレッドは、発生しない可能性のある状態の変更を待つために完全な時間を浪費します(解放するスレッドがスケジュールされるまで、 発生していません待機中のスレッドの実行中!)。したがって、any競合量が与えられた場合、ロックの取得には最適な場合に約1 1/2タイムスライスかかります(解放スレッドが次のスレッドであると想定)。これはあまり良い動作ではありません。
4。実装方法
セマフォは現在、通常sys_futex
Linuxの場合(オプションで、数回の試行後に終了するスピンロックを使用)。
スピンロックは通常、アトミック操作を使用して実装され、オペレーティングシステムによって提供されるものは使用しません。以前は、これはコンパイラ組み込み関数または移植性のないアセンブラ命令のいずれかを使用することを意味していました。一方、C++ 11とC11はどちらも言語の一部としてアトミック操作を備えているため、証明可能な正しいロックフリーコードを記述するという一般的な困難を除けば、完全に移植可能な(ほぼ)ロックフリーコードを実装できるようになりました。痛みのない方法。
非常に簡単に言えば、セマフォは「譲歩する」同期オブジェクトであり、スピンロックは「ビジーウェイト」オブジェクトです。 (単一のスレッドからコード領域を保護するミューテックス、ガード、モニター、またはクリティカルセクションとは異なり、複数のスレッドを同期するという点でセマフォにはもう少しあります)
より多くの状況でセマフォを使用しますが、非常に短時間ロックするスピンロックを使用します。ロックするのにコストがかかります。特に、頻繁にロックする場合はコストがかかります。そのような場合、保護されたリソースがロック解除されるのを待つ間、少しの間スピンロックする方が効率的です。スピンが長すぎると、明らかにパフォーマンスが低下します。
通常、スレッドクォンタムよりも長くスピンする場合は、セマフォを使用する必要があります。
Yoav Aviramとgbjbaanbが言ったことに加えて、もう1つの重要なポイントは、シングルCPUマシンでは決してスピンロックを使用しないということでしたが、セマフォはそのようなマシンでは意味があります。最近では、複数のコア、ハイパースレッディング、または同等の機能を備えたマシンを見つけるのが困難になりがちですが、CPUが1つしかない状況ではセマフォを使用する必要があります。 (理由は明らかだと信じています。単一のCPUがスピンロックを解放するために他の何かを待ってビジーであるが、唯一のCPUで実行されている場合、現在のプロセスまたはスレッドが横取りされるまでロックが解放される可能性は低いですO/S。これにはしばらく時間がかかる場合があり、プリエンプションが発生するまで何も起こりません。)
RubinniのLinuxデバイスドライバーから
セマフォとは異なり、スピンロックは割り込みハンドラなど、スリープできないコードで使用される場合があります
私はカーネルの専門家ではありませんが、いくつかのポイントがあります。
カーネルのコンパイル中にカーネルのプリエンプションが有効になっている場合、ユニプロセッサマシンでもスピンロックを使用できます。カーネルの横取りが無効になっている場合、スピンロック(おそらく)はvoidステートメントに展開されます。
また、セマフォとスピンロックを比較しようとするとき、セマフォはカーネルで使用されるものを指すと信じています-IPC(ユーザーランド)に使用されるものではありません。
基本的に、クリティカルセクションが小さく(スリープ/ウェイクアップのオーバーヘッドよりも小さく)、クリティカルセクションがスリープ可能なものを呼び出さない場合は、スピンロックを使用します。セマフォは、クリティカルセクションが大きく、スリープできる場合に使用されます。
ラマンチャロトラ。
スピンロックは、マシン依存のアセンブリ命令(テストとセットなど)を使用したスレッド間ロックの実装を指します。スレッドが単にループ内で待機(「スピン」)し、ロックが使用可能になるまで繰り返しチェックする(ビジー待機)ため、スピンロックと呼ばれます。スピンロックは、ミューテックスの代替として使用されます。ミューテックスは、短時間ロックされるとスピンロックのパフォーマンスが向上するため、オペレーティングシステム(CPUではなく)によって提供される機能です。
Semaphorは、IPC用のオペレーティングシステムによって提供される機能であるため、その主な目的はプロセス間通信です。オペレーティングシステムが提供する機能であるため、そのパフォーマンスは、(可能な場合でも)ヘッド間ロックのスピンロックほど優れていません。セマフォは、長時間のロックに適しています。
とは言うものの、アセンブリにスプリンロックを実装することは扱いにくく、移植性がありません。
Linux固有ではない、より一般的な観察結果を追加したいと思います。
メモリアーキテクチャとプロセッサの機能によっては、マルチコアシステムまたはマルチプロセッサシステムにセマフォを実装するためにスピンロックが必要になる場合があります。そのようなシステムでは、2つ以上のスレッド/プロセスが必要なときに競合状態が発生する可能性があるためですセマフォを取得します。
はい、メモリアーキテクチャが1つのコア/プロセッサによるメモリセクションのロックを提供し、他のすべてのアクセスを遅延させる場合、およびプロセッサがテストおよびセットを提供する場合、スピンロックなしでセマフォを実装できます(ただし、非常に慎重に! )。
ただし、シンプル/安価なマルチコアシステムが設計されているため(私は組み込みシステムで作業しています)、すべてのメモリアーキテクチャがこのようなマルチコア/マルチプロセッサ機能をサポートするわけではなく、テストとセットまたは同等の機能のみをサポートします。次に、実装は次のようになります。
セマフォを解放するには、次のように実装する必要があります。
はい、OSレベルでの単純なバイナリセマフォの場合は、代わりにスピンロックのみを使用できます。ただし、保護するコードセクションが非常に小さい場合のみです。
前述のように、独自のOSを実装する場合は、注意してください。このようなエラーのデバッグは楽しいですが(多くの人が共有していない私の意見ですが)、ほとんどの場合非常に面倒で困難です。
スピンロックは、スレッドの実行スライス時間が経過する前に、予想される結果が非常に短時間で発生することが確実な場合にのみ使用されます。
例:デバイスドライバーモジュールでは、ドライバーはハードウェアレジスタR0に「0」を書き込み、そのR0レジスタが1になるのを待つ必要があります。通常、これは高速です(マイクロ秒単位)。今、回転は、スリープ状態になってハードウェアによって中断されるよりもはるかに優れています。もちろん、回転中はハードウェア障害状態に注意する必要があります!
ユーザーアプリケーションがスピンする理由はまったくありません。意味がありません。何らかのイベントが発生するためにスピンし、そのイベントは、Quick Timeフレーム内で発生することが保証されていない別のユーザーレベルのアプリケーションによって完了する必要があります。そのため、ユーザーモードではまったくスピンしません。ユーザーモードでは、sleep()またはmutexlock()またはセマフォlock()を使用した方がよいでしょう。
「mutex」(または「相互排他ロック」)は、2つ以上の非同期プロセスが共有リソースを排他的に使用するために予約できる信号です。 「mutex」の所有権を取得する最初のプロセスは、共有リソースの所有権も取得します。他のプロセスは、最初のプロセスが「mutex」の所有権を解放するのを待ってから、取得を試みる必要があります。
カーネルで最も一般的なロックプリミティブはスピンロックです。スピンロックは、非常にシンプルなシングルホルダーロックです。プロセスがスピンロックを取得しようとして使用できない場合、プロセスはロックを取得できるまで試行(スピン)を続けます。この単純さにより、小さくて高速なロックが作成されます。
スピンロックとセマフォの違いは何ですか? by Maciej Piechotka :
両方とも、限られたリソースを管理します。まず、バイナリセマフォ(mutex)とスピンロックの違いについて説明します。
スピンロック ビジー待機を実行します-つまり、ループを実行し続けます:
while(try_acquire_resource()); ... release();非常に軽量なロック/ロック解除を実行しますが、ロックスレッドが同じリソースにアクセスしようとする他のスレッドによってプリエンプトされる場合、2番目のスレッドは、CPUクォンタムがなくなるまでリソースを単純に取得しようとします。
一方、ミューテックスの動作は次のようになります。if(!try_lock()){ add_to_waiting_queue(); wait(); } ... プロセス* p = get_next_process_from_waiting_queue(); p-> wakeUp();したがって、スレッドがブロックされたリソースを取得しようとすると、そのスレッドが利用可能になるまで中断されます。ロック/ロック解除ははるかに重いですが、待機は「無料」と「公正」です。
セマフォ は、複数回(初期化から既知)使用できるロックです-たとえば、3つのスレッドがリソースを同時に保持できますが、それ以上はできません。たとえば、生産者/消費者の問題や一般的なキューで使用されます。
P(resources_sem) resource = resources.pop() ... resources.Push(resources) V(resources_sem)
スピンロックは1つのプロセスのみが保持でき、セマフォは1つ以上のプロセスが保持できます。プロセスがロックを解放してからロックを取得するまで、スピンロック待機。セマフォはロックをスリープしています。つまり、待機してスリープ状態になります。