为什么这个锁定声明不起作用?

时间:2012-06-30 08:17:47

标签: c# locking

class Program
{
    static object test = new object();
    static void Main(string[] args)
    {
        new Program().test2();
        Console.ReadKey();
    }

    public void test1()
    {
        lock (test)
        {
            Console.WriteLine("test1");
        }
    }

    public void test2()
    {
        lock (test)
        {
            test1();
            Console.WriteLine("test2");
        }
    }
}

上面的代码是否应首先在test2()的lock语句中完成语句然后转到test1()? (即输出不应该是这样的吗?: TEST2 TEST1 )

3 个答案:

答案 0 :(得分:15)

没有。事件序列(标识表示调用堆栈或逻辑运算)是:

  • 主要呼叫test2
    • test2尝试获取与测试对象关联的监视器(lock语句的开头)
      • 显示器目前尚未拥有。成功了!
      • 当前线程现在“拥有”该监视器,计数为1
    • test2调用test1
      • test1尝试获取测试对象的监视器(lock语句的开头)
        • Monitor目前拥有...但是当前的线程。成功了!
        • 当前线程仍“拥有”监视器,计数为2
      • test1打印“test1”
      • test1释放监视器(lock语句的结尾)
        • 当前线程仍“拥有”监视器,计数为1
      • test1返回
    • test2打印“test2”
    • test2释放监视器(lock语句的结尾)
      • 显示器现在是无主的(因此另一个线程可以获取它)
    • test2返回

重要的是要注意监视器重入 - 如果当前线程已经拥有监视器,那么另一次获取它的尝试只会增加计数,而不是阻塞。

如果监视器不是可重入,则输出将不是“test2,test1” - 它只会是死锁。

答案 1 :(得分:11)

监视器在同一个线程上重入。非常重要的是要避免意外死锁,如果没有这种行为,你的代码就会冻结。

Mutex也是可重入的,信号量不是。

实施非常简单。监视器存储两条信息。输入它的线程的所有者Thread.ManagedId和计算输入次数的计数器。因此,第一个锁可以进入,因为它不是拥有的,它将所有者设置为您的线程并将计数设置为1.允许第二个锁进入,因为线程ID匹配,计数增加到2.结束时第二次锁定,计数再次减少到1。在第一个结束时,计数减少到0并重置所有者。

答案 2 :(得分:2)

不应该在单线程场景中使用锁定。它的目的是用于对相同方法或对象实例的跨线程调用。

您注意到的行为是正常的。

当两个或多个线程可以同时访问资源时,您通常希望同步对资源(变量,集合等)的访问。