lock语句VS Monitor.Enter方法 [英] Lock statement vs Monitor.Enter method

查看:317
本文介绍了lock语句VS Monitor.Enter方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想,这是一个有趣code例子。

I suppose that this is an interesting code example.

我们有一个类 - 我们称之为的测试 - 以敲定方式。在方式有两种,我正在使用的lock语句和 Monitor.Enter()拨打code块。另外,我在测试类在这里的两个实例。
这项实验是pretty简单:空在测试变量中锁定块,然后尝试使用 GC.Collect的方法调用手动收集。
所以,看到敲定拨打我呼吁在 GC.WaitForPendingFinalizers 方式。一切都非常简单,因为你可以看到。

We have a class -- let's call it Test -- with a Finalize method. In the Main method there are two code blocks where I am using a lock statement and a Monitor.Enter() call. Also, I have two instances of the Test class here. The experiment is pretty simple: Null the Test variable within locking block and then try to collect it manually with the GC.Collect method call. So, to see the Finalize call I am calling the GC.WaitForPendingFinalizers method. Everything is very simple, as you can see.

锁定义语句,它是由编译器打开在尝试 {...} 最后 {..}块,用 Monitor.Enter 的内部通话的尝试块和监控。然后退出在最后块。我试图实施的尝试,终于块手动。

By the definition of the lock statement, it's opened by the compiler to the try{...}finally{..} block, with a Monitor.Enter call inside of the try block and Monitor. Then it exits in the finally block. I've tried to implement the try-finally block manually.

我预计在这两种情况下相同的行为 - 在使用锁和使用的 Monitor.Enter 。但是,想不到它是不同的,你可以看到如下:

I've expected the same behaviour in both cases -- that of using lock and that of using Monitor.Enter. But, surprise, surprise it is different, as you can see below:

public class Test
{
    private string name;

    public Test(string name)
    {
        this.name = name;
    }

    ~Test()
    {
        Console.WriteLine(string.Format("Finalizing class name {0}.", name));
    }
}

class Program
{
    static void Main(string[] args)
    {
        var test1 = new Test("Test1");
        var test2 = new Test("Tesst2");
        lock (test1)
        {
            test1 = null;
            Console.WriteLine("Manual collect 1.");
            GC.Collect();
            GC.WaitForPendingFinalizers();
            Console.WriteLine("Manual collect 2.");
            GC.Collect();
        }

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

本实施例的输出是:

The output of this example is:

手动收集1.手动收集2。
  手动收集3.最终处理类
  名字的Test2。手动收集4。
  而在最后的空引用异常finally块,因为test2的是空引用。

Manual collect 1. Manual collect 2. Manual collect 3. Finalizing class name Test2. Manual collect 4. And null reference exception in last finally block because test2 is null reference.

我很惊讶,我的拆解code到IL。所以,这里是的IL转储主要方式:

I was surprised and disassembled my code into IL. So, here is the IL dump of Main method:

.entrypoint
.maxstack 2
.locals init (
    [0] class ConsoleApplication2.Test test1,
    [1] class ConsoleApplication2.Test test2,
    [2] bool lockTaken,
    [3] bool <>s__LockTaken0,
    [4] class ConsoleApplication2.Test CS$2$0000,
    [5] bool CS$4$0001)
L_0000: nop 
L_0001: ldstr "Test1"
L_0006: newobj instance void ConsoleApplication2.Test::.ctor(string)
L_000b: stloc.0 
L_000c: ldstr "Tesst2"
L_0011: newobj instance void ConsoleApplication2.Test::.ctor(string)
L_0016: stloc.1 
L_0017: ldc.i4.0 
L_0018: stloc.3 
L_0019: ldloc.0 
L_001a: dup 
L_001b: stloc.s CS$2$0000
L_001d: ldloca.s <>s__LockTaken0
L_001f: call void [mscorlib]System.Threading.Monitor::Enter(object, bool&)
L_0024: nop 
L_0025: nop 
L_0026: ldnull 
L_0027: stloc.0 
L_0028: ldstr "Manual collect."
L_002d: call void [mscorlib]System.Console::WriteLine(string)
L_0032: nop 
L_0033: call void [mscorlib]System.GC::Collect()
L_0038: nop 
L_0039: call void [mscorlib]System.GC::WaitForPendingFinalizers()
L_003e: nop 
L_003f: ldstr "Manual collect."
L_0044: call void [mscorlib]System.Console::WriteLine(string)
L_0049: nop 
L_004a: call void [mscorlib]System.GC::Collect()
L_004f: nop 
L_0050: nop 
L_0051: leave.s L_0066
L_0053: ldloc.3 
L_0054: ldc.i4.0 
L_0055: ceq 
L_0057: stloc.s CS$4$0001
L_0059: ldloc.s CS$4$0001
L_005b: brtrue.s L_0065
L_005d: ldloc.s CS$2$0000
L_005f: call void [mscorlib]System.Threading.Monitor::Exit(object)
L_0064: nop 
L_0065: endfinally 
L_0066: nop 
L_0067: ldc.i4.0 
L_0068: stloc.2 
L_0069: ldloc.1 
L_006a: ldloca.s lockTaken
L_006c: call void [mscorlib]System.Threading.Monitor::Enter(object, bool&)
L_0071: nop 
L_0072: nop 
L_0073: ldnull 
L_0074: stloc.1 
L_0075: ldstr "Manual collect."
L_007a: call void [mscorlib]System.Console::WriteLine(string)
L_007f: nop 
L_0080: call void [mscorlib]System.GC::Collect()
L_0085: nop 
L_0086: call void [mscorlib]System.GC::WaitForPendingFinalizers()
L_008b: nop 
L_008c: ldstr "Manual collect."
L_0091: call void [mscorlib]System.Console::WriteLine(string)
L_0096: nop 
L_0097: call void [mscorlib]System.GC::Collect()
L_009c: nop 
L_009d: nop 
L_009e: leave.s L_00aa
L_00a0: nop 
L_00a1: ldloc.1 
L_00a2: call void [mscorlib]System.Threading.Monitor::Exit(object)
L_00a7: nop 
L_00a8: nop 
L_00a9: endfinally 
L_00aa: nop 
L_00ab: call string [mscorlib]System.Console::ReadLine()
L_00b0: pop 
L_00b1: ret 
.try L_0019 to L_0053 finally handler L_0053 to L_0066
.try L_0072 to L_00a0 finally handler L_00a0 to L_00aa

我没有看到锁定语句,并在 Monitor.Enter 拨打任何区别。
那么,为什么我还是要的实例的引用的测试1 和对象的情况下不会被GC回收,但在使用的情况下< STRONG> Monitor.Enter 它被收集并敲定?

I don't see any difference between the lock statement and the Monitor.Enter call. So, why do I still have a reference to the instance of test1 in the case of lock, and the object is not collected by GC, but in the case of using Monitor.Enter it is collected and finalized?

推荐答案

这是因为参考指向测试1 分配给本地变量 CS $ 2 $ 0000 中的IL code。你空出C#中的测试1 变量,但锁定构造被以这样的方式编制了一个单独的参考维护。

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.

其实原因很聪明,C#编译器来实现的。否则,将有可能规避机制保障锁定语句应该强制在退出临界区释放锁的。

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.

这篇关于lock语句VS Monitor.Enter方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆