当某个线程在myList
中锁定SomeMethodA
并执行lock
内的块时,其他线程是否可以在myList.Add(1)
中执行SomeMethodB
或者它将等待因为'myList'被锁定在SomeMethodA
?
class A
{
private List<int> myList;
public void SomeMethodA()
{
lock(myList)
{
//...
}
}
public void SomeMethodB()
{
myList.Add(1);
}
}
答案 0 :(得分:6)
修改明确答案:否,您需要在SomeMethodB
中明确锁定列表。编译器不会自动为您添加锁
推荐的习语是:
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#中的锁是可重入的,因此从SomeMethodB
中的锁内调用SomeMethodA
不会死锁
更新使用私有锁object
实例背后的原理:
通常,避免锁定公共类型或超出代码控制范围的实例。常见的构造锁(this),lock(typeof(MyType))和lock(“myLock”)违反了这条准则:
如果可以公开访问实例,则
- 如果
lock (this)
会出现问题。lock (typeof (MyType))
可公开访问,则MyType
会出现问题。lock("myLock")
是一个问题,因为使用相同字符串的进程中的任何其他代码都将共享相同的锁。最佳做法是定义要锁定的私有对象,或私有静态对象变量以保护所有实例共有的数据。
请参阅:http://msdn.microsoft.com/en-us/library/c5kehkcz.aspx
1 (除了该方法的其他问题,例如空值,参考更新,死锁等)。
答案 1 :(得分:2)
锁定对象会在应用程序中全局锁定它。您甚至可以在其他类中观察锁定(如果您锁定了公共对象)。
但是,在您的示例代码中,myList.Add(1)
会不等待锁定,因为您尚未将其包装在lock
块中。
当我们说“锁定对象”或“获取对象锁定”时,它有点用词不当,因为lock
语句实际上没有要做的任何事情以防止访问对象。相反,它通过使用该对象作为“密钥”来防止多个线程进入锁定的代码块 - 并且一次只有一个线程可以拥有该密钥。因此,当您“锁定”一个对象时,所有线程仍可以自由使用该对象,但一次只有一个线程可以使用该对象进入lock
块。
答案 2 :(得分:1)
答案是否。一旦一个线程获得了对某个对象的锁(在你的情况下为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);
}
}
}
Thread1正在尝试访问SomeMethodA()
Thread2正在尝试访问SomeMethodB()
Thread3正在尝试访问SomeMethodC()
Thread1和Thread2将在不阻塞的情况下执行,但Thread3将在myList
锁定时被阻止,因为Thread1已经获取了它。
答案 3 :(得分:0)
如果你没有在SomeMethodB
内置锁,即使你在SomeMethodA
的锁中执行了一个线程,所有线程都可以访问它。为了防止线程运行SomeMethodB
,您需要像sehe一样实现您的方法。