どちらも同じ仕事をしていると思いますが、どちらを同期に使用するかをどのように決定しますか?
8 に答える
法則
理論的には、スレッドがミューテックスをロックしようとして成功しない場合、ミューテックスはすでにロックされているため、スレッドはスリープ状態になり、すぐに別のスレッドを実行できるようになります。これは、以前にロックを保持していたスレッドによってミューテックスがロック解除された場合に当てはまります。スレッドがスピンロックをロックしようとして成功しない場合、最終的に成功するまで継続的にロックを再試行します。したがって、別のスレッドが代わりになることはできません (ただし、現在のスレッドの CPU ランタイム クォンタムを超えると、オペレーティング システムは強制的に別のスレッドに切り替えます)。
問題
ミューテックスの問題は、スレッドをスリープ状態にして再び起動する操作はどちらもかなりコストがかかることです。かなり多くの CPU 命令が必要になるため、時間がかかります。ミューテックスが非常に短い時間しかロックされていなかった場合、スレッドをスリープ状態にしてから再度起動するまでにかかる時間は、スレッドが実際にスリープ状態になった時間をはるかに超え、スレッドがスリープ状態になるまでの時間をはるかに超える可能性があります。スピンロックを常にポーリングして無駄にしています。一方、スピンロックのポーリングは常に CPU 時間を浪費し、ロックが長時間保持されると、より多くの CPU 時間を浪費するため、代わりにスレッドがスリープしていた方がはるかに優れていたでしょう。
ソリューション
スピンロックのポーリングが利用可能な唯一の CPU コアをブロックしている限り、他のスレッドは実行できず、他のスレッドも実行できないため、ロックも実行できないため、シングルコア/シングル CPU システムでスピンロックを使用することは通常意味がありません。ロック解除されます。IOW、スピンロックはこれらのシステムで CPU 時間を浪費するだけで、実際の利益はありません。代わりにスレッドがスリープ状態にされた場合、別のスレッドがすぐに実行され、おそらくロックが解除され、最初のスレッドが再び起動したときに処理を続行できる可能性があります。
マルチコア/マルチ CPU システムでは、非常に短い時間だけ保持されるロックが多数あるため、スレッドを頻繁にスリープ状態にして再度起動するために無駄な時間が費やされるため、実行時のパフォーマンスが著しく低下する可能性があります。代わりにスピンロックを使用すると、スレッドは完全なランタイム クォンタムを利用する機会を得て (常に非常に短い時間のみブロックしますが、その後すぐに作業を続行します)、処理スループットが大幅に向上します。
練習
多くの場合、プログラマーはミューテックスまたはスピンロックの方が優れているかどうかを事前に知ることができません (たとえば、ターゲット アーキテクチャの CPU コアの数が不明なため)。また、オペレーティング システムも、特定のコードがシングル コアまたはマルチコア環境では、ほとんどのシステムがミューテックスとスピンロックを厳密に区別していません。実際、最近のほとんどのオペレーティング システムには、ハイブリッド ミューテックスとハイブリッド スピンロックがあります。それは実際にはどういう意味ですか?
ハイブリッド ミューテックスは、最初はマルチコア システムでスピンロックのように動作します。スレッドがミューテックスをロックできない場合、ミューテックスはすぐにロック解除される可能性があるため、すぐにはスリープ状態になりません。代わりに、ミューテックスは最初にスピンロックとまったく同じように動作します。一定の時間 (または再試行またはその他の測定要素) が経過してもロックが取得されない場合にのみ、スレッドは実際にスリープ状態になります。コアが 1 つしかないシステムで同じコードが実行される場合、mutex はスピンロックしませんが、上記のように、それは有益ではありません。
ハイブリッド スピンロックは、最初は通常のスピンロックのように動作しますが、CPU 時間を浪費するのを避けるために、バックオフ戦略を使用する場合があります。通常はスレッドをスリープ状態にしませんが (スピンロックを使用しているときにスリープ状態にさせたくないため)、スレッドを停止することを決定する場合があります (すぐに、または一定の時間が経過した後。これを「譲歩」と呼びます)。 )、別のスレッドの実行を許可するため、スピンロックのロックが解除される可能性が高くなります(スレッド切り替えのコストはかかりますが、スレッドをスリープ状態にして再度起動するコストはありません).
概要
疑わしい場合はミューテックスを使用してください。通常はミューテックスを使用することをお勧めします。最新のシステムのほとんどは、非常に短時間スピンロックできるようになっています。スピンロックを使用するとパフォーマンスが向上する場合がありますが、それは特定の条件下でのみであり、スピンロックが有益である可能性があるプロジェクトに現在取り組んでいないことを疑っているという事実は、むしろ私に教えてくれます。スピンロックまたはミューテックスを内部的に使用できる独自の「ロックオブジェクト」を使用することを検討してください (たとえば、このようなオブジェクトを作成するときにこの動作を構成できます)。最初はどこでもミューテックスを使用します。試してみて結果を比較してください (例: プロファイラーを使用)。ただし、必ず両方のケースをテストしてください。
更新: iOS に関する警告
実際には iOS 固有ではありませんが、iOS はほとんどの開発者がその問題に直面する可能性があるプラットフォームです。システムにスレッド スケジューラがある場合、スレッドの優先度がどれほど低くても、最終的に実行する機会が得られるとは限りません。スピンロックは永続的なデッドロックにつながる可能性があります。iOS スケジューラはスレッドのさまざまなクラスを区別し、下位クラスのスレッドは、上位クラスのスレッドも実行する必要がない場合にのみ実行されます。これにはバックオフ戦略がないため、高クラスのスレッドを永続的に利用できる場合、低クラスのスレッドは CPU 時間をまったく取得できず、したがって作業を実行する機会もありません。
問題は次のように表示されます。コードが優先度の低いクラスのスレッドでスピンロックを取得し、そのロックの途中でタイム クォンタムを超過し、スレッドの実行が停止します。このスピンロックを再び解放できる唯一の方法は、優先度の低いクラスのスレッドが再び CPU 時間を取得した場合ですが、これが発生する保証はありません。常に実行したいプライオリティの高いクラスのスレッドがいくつかある場合、タスク スケジューラは常にそれらを優先します。それらの 1 つがスピンロックを横切って実行し、それを取得しようとする可能性がありますが、もちろんそれは不可能であり、システムはそれを解放します。問題は、生成されたスレッドがすぐに再び実行できるようになることです! ロックを保持しているスレッドより優先順位が高いため、ロックを保持しているスレッドは CPU ランタイムを取得する機会がありません。
この問題がミューテックスで発生しないのはなぜですか? ハイプライオスレッドがミューテックスを取得できない場合、譲歩せず、少しスピンする可能性がありますが、最終的にはスリープ状態になります。スリープ状態のスレッドは、イベント (待機していたミューテックスのロックが解除されるなどのイベント) によって起動されるまで、実行できません。Apple はその問題を認識しておりOSSpinLock
、その結果として廃止されました。新しいロックは と呼ばれos_unfair_lock
ます。このロックは、異なるスレッド優先度クラスを認識しているため、上記の状況を回避します。iOS プロジェクトでスピンロックを使用することが適切であると確信している場合は、それを使用してください。近づかないOSSpinLock
! また、いかなる状況においても、独自のスピンロックを iOS に実装しないでください! 疑わしい場合は、ミューテックスを使用してください。macOS はこの問題の影響を受けません。別のスレッド スケジューラがあり、どのスレッドも (プライオリティの低いスレッドであっても) CPU 時間で「枯渇」することを許可しませんが、同じ状況がそこで発生する可能性があり、その後非常にパフォーマンスが低下する可能性があります。したがってOSSpinLock
、macOS でも非推奨です。
Mecki の提案に続き、Alexander Sandler のブログ Alex on Linux のこの記事pthread mutex vs pthread spinlockspinlock
では、#ifdef を使用して動作をテストするために&mutexes
を実装する方法を示しています。
ただし、この例は孤立したケースであり、プロジェクトの要件や環境はまったく異なる可能性があることを理解してください。
また、特定の環境や条件 (ディスパッチ レベル >= DISPATCH LEVEL で Windows を実行している場合など) では、mutex を使用できず、スピンロックを使用することにも注意してください。UNIX の場合 - 同じことです。
競合他社のstackexchange unixサイトに関する同等の質問は次のとおりです 。もっと
Windows システムでのディスパッチに関する情報: http://download.microsoft.com/download/e/b/a/eba1050f-a31d-436b-9281-92cdfeae4b45/IRQL_thread.doc
スピンロックのポーリングが利用可能な唯一の CPU コアをブロックしている限り、他のスレッドは実行できず、他のスレッドも実行できないため、ロックも実行できないため、シングルコア/シングル CPU システムでスピンロックを使用することは、通常は意味がありません。ロック解除されます。IOW、スピンロックはこれらのシステムで CPU 時間だけを浪費し、実際の利益はありません
これは間違っています。プロセスがスピンロックを取得すると、プリエンプションが無効になるため、単一プロセッサシステムでスピンロックを使用しても CPU サイクルが無駄になることはありません。ただ使っても意味がない!したがって、Uni システムのスピンロックは、コンパイル時にカーネルによって preempt_disable に置き換えられます!