非再帰ミューテックス
ほとんどのミューテックスは非再帰的です (または少なくとも非再帰的であるべきです)。ミューテックスは、アトミックに取得または解放できるオブジェクトであり、複数のスレッド間で共有されるデータを競合状態、データ破損、およびその他の厄介なものから保護できます。
1 つのミューテックスは、同じ呼び出しチェーン内の1 つのスレッドによって 1 回だけ取得される必要があります。同じスレッド コンテキスト内で同じミューテックスを 2 回取得 (または保持) しようとすると、無効なシナリオと見なされ、適切に処理される必要があります (通常は、コードの基本的な契約を破っているため、ASSERT を介して)。
再帰ミューテックス
これは、コードのにおい、またはハックと見なす必要があります。再帰的ミューテックスが標準のミューテックスと異なる唯一の点は、再帰的ミューテックスは同じスレッドによって複数回取得できることです。
再帰的ミューテックスが必要になる根本的な原因は、所有権の欠如と、クラス間の明確な目的または境界線がないことです。たとえば、コードが別のクラスを呼び出し、そのクラスが別のクラスにコールバックする場合があります。その後、開始クラスは同じミューテックスを再度取得しようとする可能性があります。クラッシュを回避したいので、これを再帰ミューテックスとして実装します。
この種の混乱したクラス階層は、あらゆる種類の頭痛の種になる可能性があり、再帰的ミューテックスは、より基本的なアーキテクチャ上の問題に対する一時的な解決策しか提供しません。
ミューテックスの種類に関係なく、同じミューテックスを取得および解放するのは常に同じスレッドである必要があります。コードで使用する一般的なパターンは次のようなものです。
Thread 1
Acquire mutex A
// Modify or read shared data
Release mutex A
Thread 2
Attempt to acquire mutex A
Block as thread 1 has mutex A
When thread 1 has released mutex A, acquire it
// Modify or read shared data
Release mutex A
同時に取得できるミューテックスが複数ある場合 (たとえば、ミューテックス A と B) はさらに複雑になります。次のようなデッドロック状態に陥るリスクがあります。
Thread 1
Acquire mutex A
// Access some data...
*** Context switch to thread 2 ***
Thread 2
Acquire mutex B
// Access some data
*** Context switch to thread 1 ***
Attempt to acquire mutex B
Wait for thread 2 to release mutex B
*** Context switch to thread 2 ***
Attempt to acquire mutex A
Wait for thread 1 to release mutex A
*** DEADLOCK ***
現在、各スレッドが他のスレッドが他のロックを解放するのを待っている状況があります。これは、 ABBA デッドロック パターンとして知られています。
この状況を回避するには、各スレッドが常に同じ順序でミューテックスを取得することが重要です (たとえば、常に A、次に B)。