3

msdnの記事スレッド同期(C#プログラミングガイド)では、次のように指定されています。

lock (x)
{
    DoSomething();
}

と同等です:

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

そしてそれ:

「lockキーワードを使用することは、Monitorクラスを直接使用するよりも一般的に好まれます。これは、保護されたコードが例外をスローした場合でも、lockによって基になるモニターが解放されることが保証されるためです。 」

このフレーズは、モニターを使用する最後のコードスニペットが、「保護されたコードが例外をスローした場合でも、基になるモニターが解放される」ことを保証しないことを意味しますか?
なぜ?

まあ、私はお互いに「同等」の主張と矛盾していることに混乱しています(ある使用法は保証し、別の使用法は同等ではありません)。

4

5 に答える 5

5

太字のテキストが参照するケースは、次のようなものです。

Monitor.Enter(obj);
DoSomethingThatThrows();
Monitor.Exit(obj);

try-finally を使用しない場合、例外をスローするとMonitor.Exit呼び出しがバイパスされます。

于 2013-02-22T08:50:44.647 に答える
3

x64 アーキテクチャ (J.Duffy による VS2008 JIT まで -スイッチAny CPUなしでコンパイルする場合、一部のまれなケースではまだ発生します) では、ステートメントとステートメント/o+の間に IL 命令が配置される可能性がありました。スタック ポインタがこの命令にあるときに例外が発生した場合、 は解放されません。Monitor.EnterTrylock

lockただし、キーワードのコード生成により、これが発生しなくなりました。

それがおそらく、lockキーワードの使用を提案する理由です。

参考文献:

Monitor.Enter、スレッドの中止

于 2013-02-22T09:13:11.427 に答える
3

ロックが提供されたコードのビットと機能的に同等である場合、finally 句があるため、ロックが解放されることは明らかです。ただし、finally を使用せずに monitor を使用すると、問題が発生し、デッドロックが発生する可能性があります。

少なくとも、それがこの記事の説明が意味していることだと私は信じています。

于 2013-02-22T08:50:38.333 に答える
3

Anycpu 用に 4.0 コンパイラによって生成された IL を見て、それを C# に逆にすると、私が得ることができる最も近いロックと同等の実装は次のようになります。

object x = new object();
bool lockTaken = false;
// lock
try{
    System.Threading.Monitor.Enter(x, ref lockTaken)
    DoSomeThing();
}
finally
{
   if (lockTaken)
   {
        System.Threading.Monitor.Exit(x);
   }
}

ロックが取得され、スレッドが中止され、ロックが解放されず、競合/デッドロックが発生する状況を防ぐためにすべてが行われます。この警告は、基本的に、適切な、そして何よりも失敗した状況で、Enter 呼び出しと Exit 呼び出しのバランスをとるように指示します。lock ステートメントは、その目標を達成するための最も単純な抽象化です。

このILに基づいて:

    IL_0000: nop
    IL_0001: ldc.i4.0
    IL_0002: stloc.0
    .try
    {
        IL_0003: ldsfld object p::x
        IL_0008: dup
        IL_0009: stloc.1
        IL_000a: ldloca.s 0
        IL_000c: call void [mscorlib]System.Threading.Monitor::Enter(object, bool&)
        IL_0011: nop
        IL_0012: nop
        IL_0013: call void p::DoSomething()
        IL_0018: nop
        IL_0019: nop
        IL_001a: leave.s IL_002c
    } // end .try
    finally
    {
        IL_001c: ldloc.0
        IL_001d: ldc.i4.0
        IL_001e: ceq
        IL_0020: stloc.2
        IL_0021: ldloc.2
        IL_0022: brtrue.s IL_002b

        IL_0024: ldloc.1
        IL_0025: call void [mscorlib]System.Threading.Monitor::Exit(object)
        IL_002a: nop

        IL_002b: endfinally
    } // end handler

    IL_002c: nop
    IL_002d: ldsfld object p::x
    IL_0032: call void [mscorlib]System.Threading.Monitor::Enter(object)
    IL_0037: nop
    .try
    {
        IL_0038: nop
        IL_0039: call void p::DoSomething()
        IL_003e: nop
        IL_003f: nop
        IL_0040: leave.s IL_0050
    } // end .try
    finally
    {
        IL_0042: nop
        IL_0043: ldsfld object p::x
        IL_0048: call void [mscorlib]System.Threading.Monitor::Exit(object)
        IL_004d: nop
        IL_004e: nop
        IL_004f: endfinally
    } // end handler

    IL_0050: nop
    IL_0051: ret
于 2013-02-22T09:48:05.010 に答える
2

これは、モニターを使用するときに、 try-finallyを使用するのを忘れる可能性があることを意味します。

実際には、多くの人が単にブロックの先頭でMonitor.Enterを使用し、ブロックの最後でMonitor.Exitを使用していることに遭遇するでしょう。例外によってブロックの途中でコードの実行が停止する可能性があるため、Monitor.Exit
が発生すると は限りません。

于 2013-02-22T08:52:15.173 に答える