msdn文章Thread Synchronization (C# Programming Guide)指定:
lock (x)
{
DoSomething();
}
相当于:
System.Object obj = (System.Object)x;
System.Threading.Monitor.Enter(obj);
try
{
DoSomething();
}
finally
{
System.Threading.Monitor.Exit(obj);
}
然后:
“使用lock关键字通常比使用Monitor更受欢迎 直接类,... 因为锁保证底层监视器 即使受保护的代码抛出异常“
,也会被释放
这句话是否意味着使用监视器的最后一个代码片段不能确保“即使受保护的代码引发异常,也会释放”基础监视器“?
为什么?
好吧,我很困惑彼此相互矛盾的“等价”而不是(一种用法保险,另一种用途,相当于,不等)。
答案 0 :(得分:5)
粗体文本所指的情况更像是这样:
Monitor.Enter(obj);
DoSomethingThatThrows();
Monitor.Exit(obj);
其中,如果没有try-finally,抛出异常将绕过Monitor.Exit
调用。
答案 1 :(得分:3)
如果lock在功能上等同于提供的代码位,则显然确保它将被释放,因为有一个finally子句。 但是,如果你最后没有使用监视器,你可能会遇到麻烦,造成死锁。
至少,我认为这篇文章的解释意味着什么。
答案 2 :(得分:3)
在x64体系结构上(根据J.Duffy,直到VS2008 JIT - 在某些极端情况下仍然会发生,在没有Any CPU
切换的情况下编译/o+
时)可能是IL指令放在Monitor.Enter
和Try
语句之间。如果堆栈指针处于此指令时发生异常,则永远不会释放lock
。
lock
关键字的代码生成阻止了这种情况的发生。
这可能就是他们建议使用lock
关键字的原因。
参考文献:
答案 3 :(得分:3)
如果您查看4.0编译器为anycpu生成的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);
}
}
完成所有操作是为了防止出现锁定,线程中止以及永远不会释放锁定的情况,从而导致争用/死锁。该警告告诉您基本上在良好和formost故障情况下平衡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
答案 4 :(得分:2)
这意味着当您使用监视器时,您可能会忘记使用 try-finally 。
实际上你会遇到很多人只是在一个块的开头使用 Monitor.Enter ,在块的末尾使用 Monitor.Exit 。 /> 这并不能保证发生 Monitor.Exit ,因为异常会导致代码在块中间停止运行。