Why does the lock insure that the underlying monitor is released and direct usage of monitor does not?

一笑奈何 提交于 2020-01-03 21:04:38

问题


The msdn article Thread Synchronization (C# Programming Guide) specifies that:

lock (x)
{
    DoSomething();
}

is equivalent to:

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

and then that:

"Using the lock keyword is generally preferred over using the Monitor class directly, ... because lock insures that the underlying monitor is released, even if the protected code throws an exception"

Does this phrase mean that the last code snippet, using the monitor, does not insure that "the underlying monitor is released, even if the protected code throws an exception" ?
And why?

Well, I am confused by the contradicting each other assertions "equivalent" and not (one usage insures and another, equivalent, does not) about the same.


回答1:


If you have a look at the IL that is generated by the 4.0 compiler for anycpu and reverse that to C# the clossest lock equivelent implementation I could get at would look like:

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);
   }
}

All is done to prevent the situation where a lock is taken, a thread aborts and the lock is never released, causing a race/deadlock. The warning tells you basically to balance Enter and Exit calls in good and formost failure situations. The lock statement is the simplest abstraction that achieves that goal.

Based on this 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



回答2:


The case that the bolded text refers to is something more like this:

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

where, without the try-finally, throwing the exception will bypass the Monitor.Exit call.




回答3:


If lock is functionally equivalent to the bit of code provided, both obviously ensure that it will be released, as there's a finally clause. However, if you just use monitor without finally, you can run into trouble, causing a deadlock.

At least, that's what I believe the article means by its explanation.




回答4:


On x64 architectures (Up until the VS2008 JIT according to J.Duffy - in some corner cases it still happens, when compiling for Any CPU without /o+ switch.) it was possible that an IL instruction was put between the Monitor.Enter and the Try statements. If an exception happened when the stack pointer was at this instruction, the lock would never be released.

The code generation for the lock keyword however prevented this from happening.

Thats probably why they suggest using the lock keyword.

References:

Monitor.Enter, Thread aborts




回答5:


It means that when you use the Monitor you might forget to use the try-finally.

Actually you'll encounter that alot of people just use Monitor.Enter in the beginning of a block and Monitor.Exit in the end of the block.
That doesn't promise the Monitor.Exit to happen, because an exception can cause the code to stop running in the middle of the block.



来源:https://stackoverflow.com/questions/15020064/why-does-the-lock-insure-that-the-underlying-monitor-is-released-and-direct-usag

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!