なぜこのコードはデッドロックを引き起こさないのですか?
private static readonly object a = new object();
...
lock(a)
{
lock(a)
{
....
}
}
スレッドがすでにロックを保持している場合、スレッドは問題なく再び「そのロックを取得」できます。
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()
を呼び出したときにセルフデッドロックが発生せず、ロックの順序が維持されます。
lock
キーワードは再入可能なロックを使用します。つまり、現在のスレッドはすでにロックを保持しているため、再取得を試みません。
デッドロックが発生するのは
スレッド1がロックAを取得
スレッド2はロックBを取得します
スレッド1はロックBの取得を試みます(スレッド2が完了するまで待機します)スレッド2はロックAの取得を試みます(スレッド1がそれで完了するまで待機します)
現在、両方のスレッドが互いに待機しているため、デッドロックしています。
C#言語仕様の セクション8.12 から:
相互排他ロックが保持されている間、同じ実行スレッドで実行されるコードは、ロックを取得して解放することもできます。ただし、他のスレッドで実行されているコードは、ロックが解放されるまでロックを取得できません。
内部lock
スコープが外部と同じスレッドにあることは明らかです。