web-dev-qa-db-ja.com

ネストされたロックがデッドロックを引き起こさないのはなぜですか?

なぜこのコードはデッドロックを引き起こさないのですか?

   private static readonly object a = new object();

...

   lock(a)
   {
      lock(a)
      {
         ....
      }
   }
49
Myster

スレッドがすでにロックを保持している場合、スレッドは問題なく再び「そのロックを取得」できます。


whyについて(そして、なぜそれが良い考えであるか)、次の状況を考えてみます。ここで、a-> bのプログラムの他の場所にロック順序が定義されています。

_void f()
{
    lock(a)
    { /* do stuff inside a */ }
}

void doStuff()
{
    lock(b)
    {
        //do stuff inside b, that involves leaving b in an inconsistent state
        f();
        //do more stuff inside b so that its consistent again
    }
}
_

おっと、ロックの順序に違反しただけで、デッドロックが発生する可能性があります。

私たちは本当に以下を実行できる必要があります:

_function doStuff()
{
    lock(a)
    lock(b)
    {
        //do stuff inside b, that involves leaving b in an inconsistent state
        f();
        //do more stuff inside b so that its consistent again
    }
}
_

これにより、f()を呼び出したときにセルフデッドロックが発生せず、ロックの順序が維持されます。

43
Anon.

lockキーワードは再入可能なロックを使用します。つまり、現在のスレッドはすでにロックを保持しているため、再取得を試みません。

デッドロックが発生するのは

スレッド1がロックAを取得
スレッド2はロックBを取得します
スレッド1はロックBの取得を試みます(スレッド2が完了するまで待機します)スレッド2はロックAの取得を試みます(スレッド1がそれで完了するまで待機します)

現在、両方のスレッドが互いに待機しているため、デッドロックしています。

18
Davy8

C#言語仕様の セクション8.12 から:

相互排他ロックが保持されている間、同じ実行スレッドで実行されるコードは、ロックを取得して解放することもできます。ただし、他のスレッドで実行されているコードは、ロックが解放されるまでロックを取得できません。

内部lockスコープが外部と同じスレッドにあることは明らかです。

8
Dan J