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(); } } |
並行コンピューティング技法 (実践マルチコア/マルチスレッドプログラミング)
関連記事
コメントを残す