web-dev-qa-db-ja.com

いつミューテックスの代わりにスピンロックを使うべきですか?

私は両方が同じ仕事をしていると思います、同期のためにどちらを使うべきかあなたはどうやって決めるのですか?

263
compile-fan

理論

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

問題

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

解決策

シングルコア/シングルCPUシステムでスピンロックを使用することは、スピンロックポーリングが唯一の利用可能なCPUコアをブロックしている限り、他のスレッドは実行できず、他のスレッドは実行できないため、通常は意味がありません。どちらかのロックを解除する。つまり、スピンロックはそれらのシステムのCPU時間だけを無駄にします。スレッドが代わりにスリープ状態にされた場合、別のスレッドが同時に実行され、ロックが解除され、最初のスレッドが再び起動したときに処理を続行できるようになる可能性があります。

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

練習

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

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

ハイブリッドスピンロックは、最初は通常のスピンロックと同じように動作しますが、CPU時間を無駄に消費し過ぎないようにするために、バックオフ戦略があります。通常はスレッドをスリープさせません(スピンロックを使用しているときには起こらないようにします)が、スレッドを停止し(すぐにまたは一定時間後に)、別のスレッドを実行できるようにすることができます。そのため、スピンロックのロックが解除される可能性が高まります(純粋なスレッド切り替えのほうが、スレッドをスリープ状態にして後で再開するのに比べて、はるかに安価です)。

要約

疑問がある場合は、ミューテックスを使用します。通常、これがより良い選択です。最近のほとんどのシステムでは、これが有益であると思われる場合、非常に短時間でスピンロックすることができます。スピンロックを使用するとパフォーマンスが向上することがありますが、特定の条件下でのみ疑わしいという事実がある場合にのみ、スピンロックが有益である可能性のあるプロジェクトに取り組んでいないことがわかります。あなたはあなた自身の "ロックオブジェクト"を使うことを考えるかもしれません、それは内部的にスピンロックかミューテックスを使うことができます(例えばこのようなオブジェクトを作成するとき設定可能です)。助けて、それを試してみて結果を比較してください(例えばプロファイラーを使用して)。しかし結論に進む前に、シングルコアとマルチコアの両方のシステムを必ずテストしてください。クロスプラットフォームになります)。

アップデート:iOSへの警告

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

問題は次のように表示されます。コードが低prioクラスのスレッドでスピンロックを取得し、そのロックの中間にある間にタイムクォンタムを超えてスレッドの実行が停止します。このスピンロックが再び解放される唯一の方法は、その低prioクラスのスレッドが再びCPU時間を取得する場合ですが、これが起こることが保証されていません。あなたは絶えず実行したい2つの高prioクラスのスレッドを持っているかもしれません、そしてタスクスケジューラは常にそれらに優先順位をつけるでしょう。そのうちの一つがスピンロックを横切って走ろうと試みるかもしれません、もちろんそれは可能ではありません、そしてシステムはそれを降伏させるでしょう。問題は、次のとおりです。生成されたスレッドはすぐに再び実行可能になります。ロックを保持しているスレッドよりも優先順位が高いため、ロックを保持しているスレッドはCPUランタイムを取得する機会がありません。他のスレッドがランタイムを取得するか、または生成されたばかりのスレッドのいずれかです。

この問題がミューテックスで発生しないのはなぜですか?高優先度スレッドがミューテックスを取得できない場合、それは譲歩しません、少し回転するかもしれませんが、最終的にはスリープ状態になります。スリープしているスレッドは、イベントによって起動されるまで実行できません。ミューテックスがロック解除されるのを待っているようなイベント。 Appleはその問題を認識しており、結果としてOSSpinLockを非推奨にしました。新しいロックはos_unfair_lockと呼ばれます。このロックは、異なるスレッド優先順位クラスを認識しているため、上記の状況を回避します。あなたのiOSプロジェクトでスピンロックを使うのが良い考えであると確信しているなら、それを使ってください。 OSSpinLockから離れてください!そして絶対にiOSにあなた自身のスピンロックを実装しないでください!疑問がある場合は、ミューテックスを使用してください。 macOSはこの問題の影響を受けません。異なるスレッドスケジューラがあるため(低優先順位のスレッドでも)CPU時間を枯渇させることはできませんが、それでも同じ状況が発生し、非常に貧弱になります。したがって、OSSpinLockはmacOSでも非推奨です。

664
Mecki

Meckiの提案を続けて、この記事 pthread mutexとpthreadスピンロック Alexander Sandlerのブログで、Alex on Linuxは、#ifdefを使ってspinlockmutexesを実装する方法を示しています。

しかし、あなたの観察に基づいて最後の電話を取ることを忘れないでください、与えられた例は孤立した場合、あなたのプロジェクト要件、環境は全く異なるかもしれないので理解してください。

7
TheCottonSilk

Meckiの答えはかなりうまくいっている。ただし、シングルプロセッサでは、タスクが割り込みサービスルーチンによって与えられるためにロックを待機しているときにスピンロックを使用すると意味があります。割り込みはISRに制御を移し、ISRは待機中のタスクが使用できるようにリソースを準備します。中断されたタスクに制御を戻す前にロックを解除することで終了します。回転するタスクはスピンロックが利用可能であることを見つけて先へ進むでしょう。

6
AlanC

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

これは競合他社のstackexchange UNIXサイトに関する同等の質問です。 https://unix.stackexchange.com/questions/5107/why-are-spin-locks-good-choices-in-linux-kernel-design-instead- of-something-more

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

6
Dan Jobs

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

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

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

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

同種のマルチコア環境では、重要なセクションに費やす時間がSpinlockの使用よりも少ない場合は、コンテキストの切り替え時間を短縮できます。 (シングルコアの比較は重要ではありません、スイッチの途中でスピンロックを実装しているシステムもあります)

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

3
Marcus Thornton