web-dev-qa-db-ja.com

再帰ミューテックスを使用する場合

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

55
jasonline

たとえば、再帰的に呼び出す関数があり、その関数への同期アクセスを取得する場合:

void foo() {
   ... mutex_acquire();
   ... foo();
   ... mutex_release();
}

再帰的なミューテックスがなければ、最初に「エントリポイント」関数を作成する必要があり、相互に再帰的な関数のセットがある場合、これは面倒になります。再帰的ミューテックスなし:

void foo_entry() {
   mutex_acquire(); foo(); mutex_release(); }

void foo() { ... foo(); ... }
48
Antti Huima

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

  • Mutexで保護されたリソースの使用から他のスレッドを除外する場合は、any mutex typeを使用できますが、オーバーヘッドが小さいため、非再帰mutexを使用できます。
  • 同じミューテックスをロックする関数を再帰的に呼び出したい場合は、どちらか
    • 1つの再帰的ミューテックスを使用する必要がある、または
    • 同じ非再帰的なミューテックスを何度もロック解除およびロックする必要があります(同時スレッドに注意してください!)(これは意味的に健全であると仮定すると、パフォーマンスの問題である可能性があります)、または
    • すでにロックされているミューテックスに注釈を付ける必要があります(再帰的な所有権/ミューテックスのシミュレーション)。
  • そのようなオブジェクトのセットから複数のミューテックスで保護されたオブジェクトをロックしたい場合、セットがマージによって構築された可能性がある場合は、を選択できます。
    • オブジェクトごとに正確に使用する1つのミューテックス、より多くのスレッドが並行して動作できるようにする、または
    • オブジェクトごとに使用する1つの参照任意の可能性のある共有再帰mutexすべてのミューテックスを一緒にロックできない、または
    • オブジェクトごとに使用する1つの比較可能な参照任意の共有される可能性がある非再帰的mutex、複数回ロックする意図。
  • ロックされているのとは別のスレッドでロックを解除する場合は、非再帰的ロック(または例外をスローする代わりに明示的に許可する再帰的ロック)を使用する必要があります。
  • 同期変数を使用する場合は、同期変数を待機しているときにミューテックスを明示的にロック解除できるである必要があります、リソースを他のスレッドで使用できるようにします。 非再帰的ミューテックスでのみ可能です。なぜなら、現在の関数の呼び出し元によって再帰的ミューテックスがすでにロックされている可能性があるからです。
23
comonad

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

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

3
FormerAtariUser

今日、再帰的なmutexの必要性に遭遇しましたが、これはこれまでに投稿された回答の中でおそらく最も単純な例だと思います。これは、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がすでに取得されているためデッドロックが発生することです。代わりに再帰ロックでロックすると、問題が修正されます。

3
Doktor Schrott

スレッドが既に所有しているミューテックスを(もう一度)取得しようとしてブロックされた場合、それは確かに問題になります...

同じスレッドがミューテックスを複数回取得できないようにする理由はありますか?

2
Michael Burr