ミューテックスの代わりにスピンロックを使用する必要があるのはいつですか?


294

両方が同じ仕事をしていると思いますが、同期に使用するものをどのように決定しますか?



11
ミューテックスとセマフォは同じものではないので、これは重複しているとは思いません。参照記事の回答には、これが正しく記載されています。詳細については、参照してくださいbarrgroup.com/Embedded-Systems/How-To/RTOS-Mutex-Semaphore
nanoquack

回答:


729

理論

理論的には、スレッドがミューテックスをロックしようとして成功しない場合、ミューテックスはすでにロックされているため、スリープ状態になり、すぐに別のスレッドを実行できます。目覚めるまでスリープ状態を続けます。これは、以前にロックを保持していたスレッドによってミューテックスがロック解除された場合に当てはまります。スレッドがスピンロックをロックしようとして成功しなかった場合、最終的に成功するまで、継続的にロックを再試行します。したがって、別のスレッドに代わることはできません(ただし、もちろん、現在のスレッドのCPUランタイムクォンタムを超えると、オペレーティングシステムは強制的に別のスレッドに切り替わります)。

問題

ミューテックスの問題は、スレッドをスリープ状態にして再び起動することはどちらもかなりコストのかかる操作であり、CPU命令を大量に必要とするため、時間がかかることです。ミューテックスが非常に短い時間だけロックされている場合、スレッドをスリープ状態にして再度ウェイクアップするのに費やされた時間は、スレッドが実際にスリープした時間を超える可能性があり、スレッドの時間を超える可能性さえありますスピンロックを常にポーリングすることで無駄になっています。一方、スピンロックのポーリングは常にCPU時間を浪費し、ロックが長時間保持されていると、CPU時間をかなり浪費し、スレッドが代わりにスリープ状態になっている場合ははるかに良くなります。

ソリューション

シングルコア/シングルCPUシステムでスピンロックを使用しても、通常は意味がありません。スピンロックポーリングが利用可能な唯一のCPUコアをブロックしている限り、他のスレッドは実行できず、他のスレッドは実行できないため、ロックは実行されません。ロック解除されます。IOW、スピンロックは、実際の利益のためにそれらのシステムのCPU時間のみを浪費します。代わりにスレッドがスリープ状態に置かれた場合、別のスレッドが一度に実行され、おそらくロックが解除され、最初のスレッドが再び目覚めたときに処理を続行できる可能性があります。

マルチコア/マルチCPUシステムでは、非常に短時間だけ保持されるロックがたくさんあるため、常にスレッドをスリープ状態にして再度ウェイクアップするために費やされる時間が無駄になり、ランタイムパフォーマンスが著しく低下する可能性があります。代わりにスピンロックを使用すると、スレッドは完全なランタイムクォンタムを利用する機会を得ます(常に非常に短い期間だけブロックし、その後すぐに作業を続行します)。これにより、処理スループットが大幅に向上します。

練習

多くの場合、プログラマーはmutexまたはスピンロックの方が優れているかどうかを事前に知ることができないため(たとえば、ターゲットアーキテクチャのCPUコアの数が不明であるため)、オペレーティングシステムが特定のコードがシングルコアまたはマルチコア環境では、ほとんどのシステムはミューテックスとスピンロックを厳密に区別しません。実際、最近のほとんどのオペレーティングシステムには、ハイブリッドmutexとハイブリッドスピンロックがあります。それは実際にはどういう意味ですか?

ハイブリッドミューテックスは、マルチコアシステムでは最初はスピンロックのように動作します。スレッドがmutexをロックできない場合、mutexはすぐにロック解除される可能性があるため、スレッドはすぐにはスリープ状態になりません。代わりに、mutexはまずスピンロックのように動作します。一定の時間(または再試行またはその他の測定要素)が経過してもロックがまだ取得されない場合にのみ、スレッドは実際にスリープ状態になります。シングルコアのみのシステムで同じコードが実行される場合、ミューテックスはスピンロックしませんが、上記のように、それは有益ではありません。

ハイブリッドスピンロックは、最初は通常のスピンロックのように動作しますが、CPU時間を浪費しすぎないようにするために、バックオフ戦略が考えられます。これは通常、スレッドをスリープ状態にしません(スピンロックを使用しているときに発生しないようにするため)が、スレッドを停止して(すぐに、または一定の時間が経過した後)、別のスレッドの実行を許可する場合があります、したがって、スピンロックがロック解除される可能性が高くなります(純粋なスレッドスイッチは通常、スレッドをスリープ状態にして後で再びウェイクアップするものよりも安価ですが、それほど遠くないですが)。

概要

疑わしい場合は、ミューテックスを使用してください。ミューテックスは通常、より良い選択です。最近のほとんどのシステムでは、有益であると思われる場合に、非常に短い時間でスピンロックできます。スピンロックを使用するとパフォーマンスが向上する場合がありますが、特定の条件下で疑わしいという事実がある場合にのみ、現在スピンロックが有益である可能性のあるプロジェクトに取り組んでいないことがわかります。内部でスピンロックまたはミューテックスを使用できる独自の「ロックオブジェクト」の使用を検討する場合があります(たとえば、このようなオブジェクトを作成するときにこの動作を構成できます)。最初はどこでもミューテックスを使用し、どこかでスピンロックを使用すると実際にはヘルプ、試して、結果を比較します(たとえば、プロファイラーを使用)。ただし、両方のケースをテストしてください。

アップデート:iOSの警告

実際にはiOS固有ではありませんが、iOSはほとんどの開発者がこの問題に直面する可能性のあるプラットフォームです。システムにスレッドスケジューラがある場合、スレッドの優先度がどれほど低くても、最終的には実行される可能性があるという保証はありません。その後、スピンロックは永続的なデッドロックを引き起こす可能性があります。iOSスケジューラーは異なるクラスのスレッドを区別し、下位クラスのスレッドは、上位クラスのスレッドも実行したくない場合にのみ実行されます。これにはバックオフ戦略がないため、高クラスのスレッドを永続的に使用できる場合、低クラスのスレッドがCPU時間を取得することがないため、作業を実行する機会がありません。

問題は次のように表示されます。コードは低プリオクラススレッドでスピンロックを取得し、そのロックの最中にタイムクォンタムを超えたため、スレッドは実行を停止します。このスピンロックを再び解放できる唯一の方法は、低プリオクラスのスレッドが再びCPU時間を取得する場合ですが、これが起こるとは限りません。常に実行したい高優先度クラスのスレッドがいくつかあり、タスクスケジューラは常にそれらを優先します。それらの1つがスピンロックを横切って実行し、それを取得しようとする可能性がありますが、これはもちろん不可能であり、システムはそれを生成します。問題は、生成されたスレッドがすぐに再び実行できるようになることです。ロックを保持しているスレッドよりも優先順位が高いため、ロックを保持しているスレッドはCPUランタイムを取得する機会がありません。

この問題がミューテックスでは発生しないのはなぜですか?ハイプリオスレッドがミューテックスを取得できない場合、ミューテックスは生成されません。少し回転する可能性がありますが、最終的にはスリープ状態になります。スリープ状態のスレッドは、イベント(たとえば、ミューテックスがロック解除されているなどのイベント)によってウェイクアップされるまで、実行できません。Appleはその問題を認識してOSSpinLockおり、結果として非推奨になりました。新しいロックはと呼ばれos_unfair_lockます。このロックは、異なるスレッド優先度クラスを認識しているため、上記の状況を回避します。iOSプロジェクトでスピンロックを使用することが適切であると確信している場合は、それを使用してください。近づかないOSSpinLock!また、iOSで独自のスピンロックを実装することは決してありません。疑わしい場合は、ミューテックスを使用してください。macOSは、スレッド(低プリオスレッドであっても)がCPU時間で「ドライ」することを許可しない別のスレッドスケジューラーを備えているため、この問題の影響を受けません。それでも同じ状況が発生し、非常に貧弱になる可能性があります。したがってOSSpinLock、macOSでも非推奨となっています。


3
すばらしい説明...スピンロックについて疑問があります。ISRでスピンロックを使用できますか?理由がない場合
ハリスは2014年

4
@Mecki私が間違っていないのであれば、タイムスライシングはシングルプロセッサシステムでのみ発生することを回答で示唆したと思います。これは正しくありません!シングルプロセッサシステムでスピンロックを使用すると、タイムクォンタムの期限が切れるまでスピンロックが行われます。次に、同じ優先度の別のスレッドが引き継ぐことができます(マルチプロセッサシステムについて説明したとおり)。
fumoboy007 14

7
@ fumoboy007 "タイムクォンタムの期限が切れるまでスピンします" //これは、CPU時間/バッテリーパワーをまったく無駄にすることなく無駄にすることを意味します。そして、いや、私はどこにもそのタイム・スライスは、単一のコアシステムで発生言っていない、私はそこにある単一コアシステム上で述べONLYありながら、タイムスライスがREALの並列オムマルチコアシステム(ともタイムスライス、まだ私は私の中で書いたものとは無関係に応答); また、ハイブリッドスピンロックとは何か、シングルおよびマルチコアシステムでうまく機能する理由についても完全に理解していません。
Mecki 14

11
@ fumoboy007スレッドAがロックを保持しており、中断されています。スレッドBが実行され、ロックが必要ですが、ロックを取得できないため、スピンします。マルチコアシステムでは、スレッドBがまだ回転している間にスレッドAを別のコアで実行し続け、ロックを解放し、スレッドBは現在のタイムクォンタム内で続行できます。シングルコアシステムでは、ロックを解除するためにスレッドAが実行できるコアは1つだけで、このコアはスレッドBの回転によってビジー状態に保持されます。したがって、スレッドBがタイムクォンタムを超える前にスピンロックを解放することはできません。したがって、すべてのスピンは時間の無駄です。
Mecki 14

2
Linuxカーネルに実装されているスピンロックミューテックスについて詳しく知りたい場合は、優れたLinuxデバイスドライバー、第3版(LDD3)の 5章(ミューテックス:109ページ、スピンロック:116ページ)を読むことを強くお勧めします。
patryk.beza 2016年

7

Meckiの提案を続けて、Alexander Sandlerのブログのこの記事pthread mutexとpthreadスピンロック、Linux上のAlexは、#ifdefを使用して動作をテストするためにspinlockmutexesを実装する方法を示しています。

ただし、得られた例は孤立したケースであり、プロジェクトの要件や環境がまったく異なる場合があるので、観察に基づいて最後の呼び出しを行うようにしてください。


6

特定の環境や条件(ディスパッチレベル> = DISPATCH LEVELのウィンドウで実行するなど)では、ミューテックスを使用できず、スピンロックを使用することにも注意してください。UNIXでも同じです。

競合他社のstackexchange UNIXサイトでの同等の質問は次のとおりです。https://unix.stackexchange.com/questions/5107/why-are-spin-locks-good-choices-in-linux-kernel-design-instead-of-something- もっと

Windowsシステムでのディスパッチに関する情報:http : //download.microsoft.com/download/e/b/a/eba1050f-a31d-436b-9281-92cdfeae4b45/IRQL_thread.doc


6

メッキーの答えはかなりうまくいきます。ただし、シングルプロセッサでは、タスクがロックが割り込みサービスルーチンによって与えられるのをタスクが待機しているときに、スピンロックを使用することが理にかなっている場合があります。割り込みにより、ISRに制御が移り、待機中のタスクがリソースを使用できるようになります。中断されたタスクに制御を戻す前にロックを解放することで終了します。回転タスクは、スピンロックが使用可能であることを検出して続行します。


2
この答えに完全に同意するかどうかはわかりません。1つのシングルプロセッサで、タスクがリソースのロックを保持している場合、ISRは安全に続行できず、タスクがリソースをロック解除するのを待つことができません(リソースを保持するタスクが中断されたため)。このような場合、タスクは割り込みを無効にして、それ自体とISRの間の除外を強制する必要があります。もちろん、これは非常に短い時間間隔で行う必要があります。
user1202136 2016

3

スピンロックとミューテックスの同期メカニズムは、今日見られるように非常に一般的です。

まず、スピンロックについて考えてみましょう。

基本的にビジーな待機アクションです。つまり、次のアクションに進む前に、指定されたロックが解放されるまで待機する必要があります。概念的には非常に単純ですが、実装はケースにありません。例:ロックが解放されていない場合、スレッドはスワップアウトされてスリープ状態になりましたが、それに対処する必要がありますか?2つのスレッドが同時にアクセスを要求した場合の同期ロックの扱い方

一般に、最も直感的なアイデアは、クリティカルセクションを保護するために変数を介して同期を処理することです。ミューテックスの概念は似ていますが、まだ異なります。焦点:CPU使用率。スピンロックは、アクションの実行を待機するためにCPU時間を消費するため、2つの違いを合計できます。

均質なマルチコア環境で、クリティカルセクションに費やす時間がスピンロックを使用するよりも小さい場合、コンテキスト切り替え時間を短縮できるためです。(一部のシステムでは、スイッチの真ん中にスピンロックが実装されているため、シングルコアの比較は重要ではありません)

Windowsでは、Spinlockを使用するとスレッドがDISPATCH_LEVELにアップグレードされます。DISPATCH_LEVELは許可されない場合があるため、今回はMutex(APC_LEVEL)を使用する必要がありました。


-6

シングルコア/シングルCPUシステムでスピンロックを使用しても、通常は意味がありません。スピンロックポーリングが利用可能な唯一のCPUコアをブロックしている限り、他のスレッドは実行できず、他のスレッドは実行できないため、ロックは実行されません。ロック解除されます。IOW、スピンロックは、実際の利益のためにそれらのシステムでCPU時間のみを浪費します

これは間違っています。プロセスがスピンロックを取得すると、プリエンプションが無効になるため、他のユーザーがスピンすることはないため、ユニプロセッサシステムでスピンロックを使用してもCPUサイクルの無駄はありません。それを使用しても意味がないというだけです!したがって、Uniシステムのスピンロックは、コンパイル時にカーネルによってpreempt_disableに置き換えられます。


引用はまだ完全に真実です。ソースコードのコンパイル結果にスピンロックが含まれていない場合、引用符は関係ありません。カーネルがスピンロックをコンパイル時に置き換えることについてあなたが言ったことが真実であるとすると、カーネル自体でのみスピンロックについて厳密に話しているのでない限り、ユニプロセッサであるかどうかに関係なく、別のマシンでプリコンパイルされたときにスピンロックはどのように処理されますか?
Hydranix 2016年

「プロセスがスピンロックを取得すると、プリエンプションは無効になります」。プロセスがスピンする場合、プリエンプションは無効になりません。その場合、単一のプロセスがスピンロックに入り、決して終了しないことにより、マシン全体を停止させる可能性があります。スレッドが(ユーザースペースではなく)カーネルスペースで実行されている場合、スピンロックを取得すると、実際にはプリエンプションが無効になりますが、ここではそれを説明していません。
Konstantin Weitz 2016

で、コンパイル時によってカーネル
Shien

@konstantin FYIスピンロックはカーネル空間でのみ取得できます。また、スピンロックが取得されると、ローカルプロセッサでプリエンプションが無効になります。
Neelansh Mittal

@hydranixうまくいきませんでしたか?CONFIG_SMPが有効になっているカーネルに対してモジュールをコンパイルし、CONFIG_SMPが無効になっているカーネルで同じモジュールを実行することはできません。
Neelansh Mittal
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.