[C#]lock文で排他をとる仕組みがC#4.0で変更された件

C#では、オブジェクトの排他を取るためにlock文があります。

lock( obj ) {
    body;
}



これは、C#4.0の場合は以下のコードと等価です。

bool lockWasTaken = false;
var temp = obj;
try {
    Monitor.Enter(temp, ref lockWasTaken);
    {
        body;
    }
} finally {
    if (lockWasTaken) {
        Monitor.Exit(temp);
    }
}




わざわざ「C#4.0の場合は」と注意書きしたのは、C#3.0以前では下記のコードと等価だったからです。

var temp = obj;
Monitor.Enter(temp);
try {
    body;
} finally {
    Monitor.Exit(temp);
}



どちらでも一緒のように見えますが、後者のコードだとMonitor.Enter(temp);とtryの間に、コンパイラが内部的にIL命令を挿入しており、かつ、そこで例外が発生した時に、Lockの解除が行われないというリスクを抱えています。ロックの解除が行われないと、結果としてデッドロックの原因になってしまいます。

C# 4.0では、この問題を解消するために等価となるコードが変更されました。


と、ここまでlock()と、”等価となるコード”と今まで書いていましたが…
実はlock()文は、上記コードのsyntax sugerなので、完全に同一のものです(コンパイル前に置換されると分かりやすいです)。



また余談ですが、Monitor.Enter()メソッドは、”スレッドに対して”排他を提供します。
ですので、下記のコードではデッドロックは発生しません。
要は、単一スレッドなら、同一オブジェクトを複数回Monitor.Enter()を行えるという事です。

SomeObj obj;
 
void MainMethod( void ) {
    lock( obj ) {
        SubMethod();
    }
}
 
void SubMethod( void ) {
    lock( obj ) {
        DoSomething();
    }
}



並行コンピューティング技法 (実践マルチコア/マルチスレッドプログラミング)

関連記事

コメントを残す

メールアドレスが公開されることはありません。