在一种方法中锁定某些对象也使其锁定在另一种方法中
本文关键字:锁定 方法 对象 另一种 一种 | 更新日期: 2023-09-27 18:36:09
当某个线程锁定myList
SomeMethodA
并在lock
内执行块时,其他线程是否可以在SomeMethodB
中执行myList.Add(1)
,或者它会等待,因为"myList"被锁定在SomeMethodA
中?
class A
{
private List<int> myList;
public void SomeMethodA()
{
lock(myList)
{
//...
}
}
public void SomeMethodB()
{
myList.Add(1);
}
}
编辑 明确答案:不,您需要在SomeMethodB
中明确锁定列表。编译器不会自动为您添加锁
- 否则,您为什么必须显式锁定?
- 事情会非常缓慢。只禁止多线程比总是锁定每个对象访问要好得多1
推荐的成语是这样的:
class A
{
private List<int> myList;
private readonly object _lockObject = new Object();
public void SomeMethodA()
{
lock(_lockObject)
{
//...
}
}
public void SomeMethodB()
{
lock(_lockObject)
{
myList.Add(1);
}
}
}
当心像这样暴露细粒度锁定(只要锁下不发生阻塞操作,您通常希望执行粗粒度锁定)。
注意 但是,C# 中的锁是可重入的,因此从锁中的SomeMethodA
中调用SomeMethodB
不会死锁
更新使用私有锁object
实例背后的基本原理:
通常,请避免锁定公共类型或代码无法控制的实例。常见的构造 lock (this)、lock (typeof (MyType)) 和 lock ("myLock") 违反了以下准则:
- 如果可以公开访问实例,则
lock (this)
是一个问题。- 如果
MyType
可公开访问,则lock (typeof (MyType))
是一个问题。lock("myLock")
是一个问题,因为进程中使用相同字符串的任何其他代码都将共享相同的锁。最佳做法是定义要锁定的私有对象,或定义私有静态对象变量以保护所有实例通用的数据。
请参阅:http://msdn.microsoft.com/en-us/library/c5kehkcz.aspx
1(除了该方法的其他问题,例如空值、引用更新、死锁等)
锁定对象会在应用程序中全局锁定对象。您甚至可以观察其他类中的锁定(如果您锁定了公共对象)。
但是,在您的示例代码中,myList.Add(1)
不会等待锁,因为您尚未将其包装在 lock
块中。
或"获取对象锁定"时,这有点用词不当,因为lock
语句实际上与阻止访问对象没有任何关系。相反,它通过使用该对象作为"键"来防止多个线程进入锁定的代码块 - 并且一次只有一个线程可以拥有该键。因此,当您"锁定"一个对象时,所有线程仍然可以自由使用该对象,但一次只有一个线程可以使用该对象进入lock
块。
答案是否定的。一旦一个线程获得了某个对象的锁(在您的情况下myList
),没有其他线程可以访问该对象的锁,Thread1 将阻止所有其他线程,直到 Thread1 释放myList
锁,但条件是如果其他线程也在尝试锁定。
在您的示例中,如果 Thread1 正在执行SomeMethodA()
(其锁为 myList
),并且 Thread2 正在执行SomeMethodB()
(它不请求锁定),则不会有任何问题,它们不会相互阻塞。
请考虑以下示例以获得更多说明。
class A
{
private List<int> myList;
public void SomeMethodA()
{
lock (myList)
{
//...
}
}
public void SomeMethodB()
{
myList.Add(1);
}
public void SomeMethodC()
{
lock (myList)
{
myList.Add(2);
}
}
}
线程 1 正在尝试访问SomeMethodA()
线程 2 正在尝试访问SomeMethodB()
线程 3 正在尝试访问SomeMethodC()
1 和线程 2 将在不相互阻塞的情况下执行,但线程 3 将在myList
锁定时被阻止,因为这已被线程 1 获取。
如果你没有在SomeMethodB
里面放一个锁,所有线程都可以访问它,即使你有一个线程在SomeMethodA
的锁中执行。为了防止线程运行SomeMethodB
你需要像sehe一样实现你的方法。