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