許可されることは有用であるため、そうすることはそれほど有用ではありません。他のパブリックメソッドを呼び出すパブリックメソッドを頻繁に使用する方法を検討してください。パブリックメソッドがロックを呼び出し、パブリックメソッドがそれを呼び出すことで、それが行うことのより広い範囲をロックする必要がある場合、再帰ロックを使用できるということは、そうすることができることを意味します。
2つのロックオブジェクトを使用したいと思う場合もありますが、それらを一緒に使用するため、間違えるとデッドロックの大きなリスクがあります。ロックに与えられているより広い範囲に対処できる場合は、両方の場合に同じオブジェクトを使用し、両方のオブジェクトを使用する場合は繰り返し使用すると、これらの特定のデッドロックが削除されます。
でも...
この有用性については議論の余地があります。
最初のケースでは、ジョー・ダフィーから引用します:
再帰は通常、同期設計が過度に単純化されていることを示しており、コードの信頼性が低下することがよくあります。一部の設計では、機能がロックを取得する機能と、ロックがすでに取得されていると想定する機能に分割されないようにする方法として、ロック再帰を使用しています。これは確かにコードサイズの縮小につながる可能性があり、したがって書き込み時間の短縮につながる可能性がありますが、最終的にはより脆弱な設計になります。非再帰的なロックを取得するパブリックエントリポイントにコードを因数分解し、ロックをアサートする内部ワーカー関数を保持することをお勧めします。再帰的ロック呼び出しは冗長な作業であり、生のパフォーマンスのオーバーヘッドに寄与します。しかし、さらに悪いことに、再帰によっては、プログラムの同期動作、特に不変条件が保持するはずの境界を理解するのが難しくなる可能性があります。通常、ロック取得後の最初の行は、オブジェクトの不変の「セーフポイント」を表していると言いたいのですが、再帰が導入されるとすぐに、このステートメントを自信を持って作成できなくなります。これにより、動的に構成されたときに正しく信頼できる動作を保証することがより困難になります。
(Joeは、彼のブログの他の場所、および並行プログラミングに関する彼の本で、このトピックについてさらに詳しく述べています)。
2番目のケースは、再帰的なロックエントリによってさまざまなタイプのデッドロックが発生する場合、または競合率が非常に高くなり、デッドロックが発生する可能性がある場合とバランスが取れています(この男は、デッドロックをヒットするだけの方がいいと言っています初めて再発したときは、私は同意しません-素晴らしいスタックトレースでアプリをダウンさせた大きな例外をスローするだけの方がいいと思います)。
悪いことの1つは、間違ったタイミングで単純化されることです。コードを記述しているときは、物事をより細かく分割して、いつロックする必要があるかについてより深く考えるよりも、ロック再帰を使用する方が簡単です。ただし、コードをデバッグしている場合、ロックを解除しても、そのロックを解除しても問題が複雑になるわけではありません。なんて悪い方法でしょう。私たちが何をしているのかを知っているとき、その複雑なコードは、時間外に楽しんで、時間に甘んじないように誘惑することであり、私たちがそれを台無しにしたことに気付いたときです。私たちは物事が素晴らしくシンプルであることを最も望んでいます。
あなたは本当にそれらを条件変数と混ぜたくありません。
ねえ、POSIXスレッドはあえてそれらを持っているだけです!
少なくともlock
キーワードはMonitor.Exit()
、すべてMonitor.Enter()
のsに一致するsがない可能性を回避することを意味します。これにより、リスクの一部が発生する可能性が低くなります。そのモデルの外で何かをする必要がある時まで。
最近のロッククラスでは、.NETは、古いコーディングパターンを使用するユーザーをブロックすることなく、ロック再帰の使用を回避するのに少し役立ちます。ReaderWriterLockSlim
再帰を使用できるコンストラクターのオーバーロードがありますが、デフォルトはですLockRecursionPolicy.NoRecursion
。
多くの場合、並行性の問題に対処する際には、より優れた並行性をもたらす可能性があるが、正確性を確認するためにより多くの注意が必要な、より複雑な手法と、より悪い並行性をもたらす可能性があるが、正確さを確認する方が簡単です。ロックを再帰的に使用すると、ロックをより長く保持し、同時実行性が低下し、正確性の確信が薄れ、デバッグが困難になるという手法が得られます。