Lock statement vs Monitor.Enter method

前端 未结 2 940
情歌与酒
情歌与酒 2020-12-02 16:43

I suppose that this is an interesting code example.

We have a class -- let\'s call it Test -- with a Finalize method. In the

相关标签:
2条回答
  • 2020-12-02 17:34

    I do not see any difference between lock statement and Monitor.Enter call.

    Look more carefully. The first case copies the reference to a second local variable to ensure that it stays alive.

    Notice what the C# 3.0 spec says on the subject:

    A lock statement of the form "lock (x) ..." where x is an expression of a reference-type, is precisely equivalent to

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

    except that x is only evaluated once.

    It's that last bit -- except that x is only evaluated once -- that is the key to the behaviour. In order to ensure that x is evaluated only once we evaluate it once, store the result in a local variable, and re-use that local variable later.

    In C# 4 we've changed the codegen so that it is now

    bool entered = false;
    try { 
      System.Threading.Monitor.Enter(x, ref entered);
      ... 
    }
    finally { if (entered) System.Threading.Monitor.Exit(x); }
    

    but again, x is only evaluated once. In your program you are evaluating the lock expression twice. Your code really should be

        bool lockTaken = false;   
        var temp = test2;
        try {   
            System.Threading.Monitor.Enter(temp, ref lockTaken);   
            test2 = null;   
            Console.WriteLine("Manual collect 3.");   
            GC.Collect();   
            GC.WaitForPendingFinalizers();   
            Console.WriteLine("Manual collect 4.");   
            GC.Collect();   
        }   
        finally {   
           System.Threading.Monitor.Exit(temp);   
        }  
    

    Now is it clear why this works the way it does?

    (Also note that in C# 4 the Enter is inside the try, not outside as it was in C# 3.)

    0 讨论(0)
  • 2020-12-02 17:43

    It is because the reference pointed to by test1 is assigned to the local variable CS$2$0000 in the IL code. You null out the test1 variable in C#, but the lock construct gets compiled in such a manner that a separate reference is maintained.

    It is actually quite clever that the C# compiler does this. Otherwise it would be possible to circumvent the guarentee the lock statement is supposed to enforce of releasing the lock upon exiting the critical section.

    0 讨论(0)
提交回复
热议问题