如果实例可公开访问,则使用lock(this)

时间:2016-10-17 15:03:31

标签: c#

我正在寻找不使用"这个"在锁中,如果实例是公共可访问的。我尝试下面的例子,我认为不会调用Method1,因为已经在实例的Main方法中获取了锁。但Method1被称为Method2无限期等待。对此的解释将不胜感激。

class Program
{
    static void Main(string[] args)
    {
        Tracker tracker = new Tracker();
        lock (tracker)
        {
            Parallel.Invoke(() => tracker.Method1(),
                () => tracker.Method2());
        }
    }
}

class Tracker
{
    private int number = 6;
    public void Method1()
    {
        lock (this)
        {
            number *= 5;
            Console.WriteLine("Method1: " + number);
            number /= 4;
            Console.WriteLine("Method1: " + number);
        }
    }

    public void Method2()
    {
        lock (this)
        {
            number *= 3;
            Console.WriteLine("Method2: " + number);
            number /= 2;
            Console.WriteLine("Method2: " + number);
        }
    }
}

2 个答案:

答案 0 :(得分:6)

不可能锁定一个可公开访问的对象,这只是一个非常糟糕的主意,因为它可以更好地理解对象更多的同步。通常的做法是永远不要锁定在当前类型范围之外可以访问的对象,因为它可以很容易地跟踪可以获取锁的所有可能位置,这使得它更容易诊断同步的潜在问题,并在查看代码时了解正在同步的内容。

当您在公开公开的对象上进行同步时,它将工作,这只是意味着如果遇到问题,您将拥有巨大的空间通过试图弄清楚有问题的相互作用是什么。当非常不同的地方的许多不同代码都与被锁定的对象进行交互时,它也会更容易导致死锁。

答案 1 :(得分:3)

来自Parallel.Invoke的文档:

  

并行执行每个提供的操作可能

所以这就是发生的事情:

  • 您的第一个操作在与主函数调用相同的线程中运行
  • Locks in C# are re-entant ,因此Method1可以重新获取锁定。

以下代码打印线程id-s以证明MainMethod1在同一个线程中运行。

class Program
{
    static void Main(string[] args)
    {
        Tracker tracker = new Tracker();
        Console.WriteLine("Main TID: " + Thread.CurrentThread.ManagedThreadId);
        lock (tracker)
        {
            Console.WriteLine("Main Acquired");
            Parallel.Invoke(() => tracker.Method1(),
                () => tracker.Method2());
        }
    }
}

class Tracker
{
    private int number = 6;
    public void Method1()
    {
        Console.WriteLine("Method1 TID: " + Thread.CurrentThread.ManagedThreadId);
        lock (this)
        {
            Console.WriteLine("Method1 Acquired");
        }
    }

    public void Method2()
    {
        Console.WriteLine("Method2 TID: " + Thread.CurrentThread.ManagedThreadId);
        lock (this)
        {
            Console.WriteLine("Method2 Acquired");
        }
    }
}

输出

Main TID: 1
Main Acquired
Method1 TID: 1
Method1 Acquired
Method2 TID: 3

然后它挂了。 Thread ID - s显示Method1Main在同一个主题中运行。