4

.NET Framework 4.5 のMonitorドキュメントで、 lock キーワードが Monitor のEnter(Object, Boolean)メソッドを使用するという文を見つけました。

Enter メソッドと Exit メソッドによって提供される機能は、C# の lock ステートメント (Visual Basic では SyncLock) によって提供される機能と同じですが、lock と SyncLock が Enter(Object, Boolean) メソッドのオーバーロードと Exit メソッドを try…finally でラップする点が異なります。ブロック (Visual Basic では Try…Finally) を使用して、モニターが確実に解放されるようにします。

一方、モニターの概要には次のようなものがあります。

Visual Basic の SyncLock および C# のロック ステートメントでは、MonitorEnter を使用してロックを取得し、MonitorExit を使用して解放します。

上記の MonitorEnter は、以前とは異なるバージョンの Enter メソッドを参照しています。つまり、Enter(Object)

Visual Studio 2012のスレッド同期 (C# および Visual Basic)には、ロックがモニターをラップする方法のサンプルがあります。

System.Object obj = (System.Object)x;
System.Threading.Monitor.Enter(obj);
try
{
    DoSomething();
}
finally
{
    System.Threading.Monitor.Exit(obj);
}

Enter(Object)版もあります。

何が本当?lock ステートメントはEnter(Object, Boolean)またはEnter(Object)を呼び出しますか? 実際のやり方に違いはあるのでしょうか。

4

1 に答える 1

6

Eric Lippert のブログから:

lock(obj){body}[C# 3.0 以前] の構文糖衣だったことを思い出してください。

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

ここでの問題は、コンパイラがモニターの開始と try 保護領域の間でノーオペレーション命令を生成した場合、モニターの開始後、試行の前に、ランタイムがスレッド アボート例外をスローする可能性があることです。そのシナリオでは、finally が実行されないため、ロックがリークし、最終的にプログラムがデッドロックする可能性があります。最適化されていないビルドと最適化されたビルドでこれが不可能であればいいのですが。

C# 4.0 では、lock を変更して、あたかもロックされているかのようにコードを生成するようにしました。

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

問題は今や他人の問題になります。Monitor.Enter の実装は、フラグを台無しにするスレッド アボート例外の影響を受けない方法でフラグをアトミックに設定する責任を負います。

それで、今はすべて順調ですよね?

悲しいことに、いいえ。[...]

一方、C# 4.0 言語仕様には次のように書かれています。

フォームのロックステートメント

lock (x) ...

ここで、x は参照型の式であり、正確に同等です

System.Threading.Monitor.Enter(x);
try {
   ...
}
finally {
   System.Threading.Monitor.Exit(x);
}

ただし、x は 1 回だけ評価されます。

于 2013-02-22T12:15:03.987 に答える