68

再帰ミューテックスを使用すると、デッドロックに陥ることなくミューテックスを複数回ロックでき、同じ回数ロックを解除する必要があることを理解しています。しかし、再帰ミューテックスを使用する必要があるのは、具体的にどのような状況でしょうか? デザイン/コードレベルの状況を探しています。

4

8 に答える 8

23

再帰的ミューテックスと非再帰的ミューテックスには、異なるユースケースがあります。ミューテックスタイプは、他のタイプを簡単に置き換えることはできません。非再帰的ミューテックスはオーバーヘッドが少なく、再帰的ミューテックスは、ある状況では有用または必要なセマンティクスを持ち、他の状況では危険なまたは壊れたセマンティクスを持ちます。ほとんどの場合、誰かが再帰的ミューテックスを使用する戦略を、非再帰的ミューテックスの使用に基づいた、より安全で効率的な別の戦略に置き換えることができます。

  • 他のスレッドをミューテックスで保護されたリソースの使用から除外したい場合は、任意のミューテックスタイプを使用できますが、オーバーヘッドが小さいため、非再帰的なミューテックスを使用することをお勧めします。
  • 同じミューテックスをロックする関数を再帰的に呼び出したい場合は、次のいずれかを実行します。
    • 1つの再帰的ミューテックスを使用する必要があります。
    • 同じ非再帰的ミューテックスを何度もロック解除およびロックする必要があります(同時スレッドに注意してください!)(これが意味的に健全であると仮定すると、パフォーマンスの問題である可能性があります)、または
    • すでにロックしているミューテックスに何らかの方法で注釈を付ける必要があります(再帰的な所有権/ミューテックスをシミュレートします)。
  • そのようなオブジェクトのセットから複数のミューテックスで保護されたオブジェクトをロックする場合は、セットをマージして構築できた可能性があります。
    • オブジェクトごとに正確に1つのミューテックスを使用して、より多くのスレッドを並行して動作させる、または
    • オブジェクトごとに、共有されている可能性のある再帰的ミューテックスへの1つの参照を使用する、すべてのミューテックスを一緒にロックできない可能性を下げる、または
    • オブジェクトごとに、共有されている可能性のある非再帰的ミューテックスへの1つの同等の参照を使用し、複数回ロックする意図を回避します。
  • ロックされているスレッドとは異なるスレッドでロックを解放する場合は、非再帰ロック(または例外をスローする代わりにこれを明示的に許可する再帰ロック)を使用する必要があります。
  • 同期変数を使用する場合は、同期変数を待機している間、ミューテックスのロックを明示的に解除できる必要があります。これにより、リソースを他のスレッドで使用できるようになります。これは、非再帰的ミューテックスでのみ可能です。再帰的ミューテックスは、現在の関数の呼び出し元によってすでにロックされている可能性があるためです。
于 2011-04-02T15:40:43.660 に答える
7

今日、再帰ミューテックスの必要性に遭遇しました。これまでに投稿された回答の中でおそらく最も単純な例だと思います。これは、Process(...) と reset() の 2 つの API 関数を公開するクラスです。

public void Process(...)
{
  acquire_mutex(mMutex);
  // Heavy processing
  ...
  reset();
  ...
  release_mutex(mMutex);
}

public void reset()
{
  acquire_mutex(mMutex);
  // Reset
  ...
  release_mutex(mMutex);
}

クラスの内部を変更するため、両方の関数を同時に実行してはならないため、ミューテックスを使用したかったのです。問題は、Process() が内部で reset() を呼び出すことです。mMutex が既に取得されているため、デッドロックが発生します。代わりに、再帰ロックでそれらをロックすると、問題が修正されます。

于 2015-05-08T17:16:01.933 に答える
3

再帰的ミューテックスを使用するコードの例を見たい場合は、Linux/Unix 用の「Electric Fence」のソースを参照してください。これは、 Valgrindが登場する前は、「境界チェック」の読み取り/書き込みオーバーランとアンダーランを検出し、解放されたメモリを使用するための一般的な Unix ツールの 1 つでした。

ソースを使用して電気フェンスをコンパイルしてリンクし (gcc/g++ を使用したオプション -g)、リンク オプション -lefence を使用してソフトウェアとリンクし、malloc/free への呼び出しをステップ実行するだけです。http://elinux.org/Electric_Fence

于 2012-04-27T15:44:27.087 に答える