C# 适当的锁定

C# proper locking

假设我执行了这样的操作:

A.B.C.add(new D());
A.B.add(new C));
A.add(new B));

这些操作同时执行。想象一下,这些是三种不同的方法。

lock(A.B.C)
   A.B.C.add(new D());

lock(A.B)
   A.B.add(new C));

lock(A)
   A.add(new B));

这些锁是独立的吗?换句话说 - 这些动作会同时执行,还是仅在前一个动作完成后才执行?

如果正确 - 这是设置锁的最有效方法吗?

编辑

我有很多这样的调用(这 3 种方法被调用了很多次)。

是的,锁是相互独立的。 lock 语句实际上在参数实例上创建了一个监视器,它没有考虑您实际到达那里的方式(即 属性 链 A.B.C)。

但是,您的代码存在一个微妙的问题。由于锁是独立的,并且三个锁语句可能同时执行,任何像

这样的语句
lock(A.B.C)
    A.B.C.Add(new D());

是危险的,因为当获取锁时,属性 A.B.C 可能已经改变。因此,为此使用局部变量会更好(即更安全):

var c = A.B.C;
lock(c)
    c.Add(new D());

这些语句将同时执行。您可以简单地通过 运行 下面的代码进行测试,并看到控制台写入全部立即发生:

Task.Run(() =>
{
    lock (a.B.C)
    {
        Console.WriteLine("a.B.C");
        Thread.Sleep(5000);
    }
});

Task.Run(() =>
{
    lock (a.B)
    {
        Console.WriteLine("a.B");
        Thread.Sleep(5000);
    }
});

Task.Run(() =>
{
    lock (a)
    {
        Console.WriteLine("a");
        Thread.Sleep(5000);
    }
});

来自C# reference

Best practice is to define a private object to lock on

所以在这种情况下,大致如下:

public class A
{
    private readonly object _syncLock = new object();
    public B B { get; private set; }

    public A()
    {
    }

    public void Add(B b)
    {
        lock (_syncLock)
        {
            // The actual logic
        }
    } 
}

这样线程安全由 Add 方法本身处理,因此它不是外部调用的问题。