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
方法本身处理,因此它不是外部调用的问题。
假设我执行了这样的操作:
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
方法本身处理,因此它不是外部调用的问题。