Atomicity & Asynchronous Exception Failures
このような非同期例外への対処のガイドラインとして,Joe Duffy 氏の非常に参考になる記事が存在します.
Atomicity & Asynchronous Exception Failures
finally ブロックによる非同期例外の遅延に関する話もちゃんと紹介されています*1.
この記事には他にも色々な問題が紹介されていますが,その中に C# の lock 構文も同様の問題を抱えているとあります.
lock 構文は以下のように展開されます.
System.Threading.Monitor.Enter(x); try { ... } finally { System.Threading.Monitor.Exit(x); }
よく見ると分かるように,Monitor.Enter の実行から try ブロックに入るまでの間にやはり若干の隙間が存在します.やはりこの間に非同期例外が発生する可能性があります.
このような問題を避けるため,CLR 2.0 の内部では次のような方法で lock を行っているそうです.
bool took = false; try { Monitor.ReliableEnter(foo, out took); // code inside synchronized block } finally { if (took) Monitor.Exit(foo); }
ただし Monitor.ReliableEnter は internal メソッドで,残念ながら我々は使うことができません.
対処方法は何個か考えられますが,例えば Constrained Execution Regions (CERs) を使用して非同期例外を排除するという方法があるでしょう.
(2009年10月20日追記)
.NET Framework 4.0 では Monitor.Enter に新しいオーバーロードが追加され,この問題は回避可能になるようです.それにともない,C# 4.0 コンパイラが出力する IL も変化するとのこと.
*1:3. Don’t use finally blocks to intentionally delay asynchronous exceptions.