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

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

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

ベストアンサー1

その理論

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

問題

ミューテックスの問題は、スレッドをスリープ状態にして再び起動させる操作がどちらもかなりコストのかかる操作であり、かなりの CPU 命令が必要になり、時間もかかることです。ミューテックスがほんの短い時間しかロックされていなかった場合、スレッドをスリープ状態にして再び起動させるのにかかる時間は、スレッドが実際にスリープしていた時間よりはるかに長くなり、スピンロックを絶えずポーリングすることでスレッドが無駄にしていた時間より長くなる可能性があります。一方、スピンロックをポーリングすると CPU 時間が絶えず無駄になり、ロックが長時間保持されると、CPU 時間がさらに無駄になり、スレッドをスリープ状態にした方がはるかに良いでしょう。

ソリューション

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

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

練習

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

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

ハイブリッド スピンロックは、最初は通常のスピンロックのように動作しますが、CPU 時間を無駄にしすぎないように、バックオフ戦略を採用する場合があります。通常はスレッドをスリープ状態にしません (スピンロックの使用時にスリープ状態になることは望ましくないため)。ただし、スレッドを停止 (即時または一定時間経過後。これを「譲歩」と呼びます) して別のスレッドを実行できるようにする場合があります。これにより、スピンロックがロック解除される可能性が高まります (スレッド切り替えのコストは依然として発生しますが、スレッドをスリープ状態にして再度起動するコストは発生しません)。

まとめ

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

アップデート: iOS に関する警告

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

問題は次のように発生します。コードが低優先度クラスのスレッドでスピンロックを取得し、そのロックの途中でタイム クォンタムが超過し、スレッドの実行が停止します。このスピンロックを再度解放する唯一の方法は、その低優先度クラスのスレッドが再び CPU 時間を取得することですが、これが起こることは保証されていません。常に実行したい高優先度クラスのスレッドが 2 つある場合があり、タスク スケジューラは常にそれらを優先します。そのうちの 1 つがスピンロックに遭遇して取得しようとする可能性がありますが、もちろんこれは不可能であり、システムはスレッドを譲ります。問題は、譲ったスレッドがすぐに再度実行可能になることです。ロックを保持しているスレッドよりも優先度が高いため、ロックを保持しているスレッドは CPU ランタイムを取得する機会がありません。他のスレッドがランタイムを取得するか、譲ったばかりのスレッドが取得します。

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

おすすめ記事