到目前为止,我对多种方法使用相同的锁没有任何问题,但我想知道下面的代码是否可能确实存在我不知道的问题(性能?):
private static readonly object lockObj = new object();
public int GetValue1(int index)
{
lock(lockObj)
{
// Collection 1 read and/or write
}
}
public int GetValue2(int index)
{
lock(lockObj)
{
// Collection 2 read and/or write
}
}
public int GetValue3(int index)
{
lock(lockObj)
{
// Collection 3 read and/or write
}
}
无论如何,3种方法和集合都无关。
此外,如果单身人士(在Instance属性中)也使用lockObj
这会是一个问题吗?
编辑:澄清我在Singleton类中使用相同锁对象的问题:
private static readonly object SyncObject = new object();
public static MySingleton Instance
{
get
{
lock (SyncObject)
{
if (_instance == null)
{
_instance = new MySingleton();
}
}
return _instance;
}
}
public int MyMethod()
{
lock (SyncObject)
{
// Read or write
}
}
这会导致问题吗?
答案 0 :(得分:16)
如果方法与您所说的无关,则为每个方法使用不同的锁;否则它效率低下(因为没有理由让不同的方法锁定同一个对象,因为它们可以安全地同时执行)。
此外,似乎这些是锁定在静态对象上的实例方法 - 这是有意的吗?我有一种感觉,这是一个错误;实例方法应该(通常)只锁定实例字段。
关于Singleton设计模式:
虽然锁定对于那些人来说是安全的,但更好的做法是对字段进行延迟初始化,如下所示:
private static object sharedInstance;
public static object SharedInstance
{
get
{
if (sharedInstance == null)
Interlocked.CompareExchange(ref sharedInstance, new object(), null);
return sharedInstance;
}
}
这种方式更快一些(因为互锁方法更快,并且因为初始化延迟了),但仍然是线程安全的。
答案 1 :(得分:8)
通过在所有这些方法中使用相同的对象lock
,您可以序列化对所有线程中代码的所有访问。
这就是......运行GetValue1()
的代码将阻止不同线程中的其他代码运行GetValue2()
,直到完成为止。如果你添加更多锁定在同一个对象实例上的代码,你最终会在某个时候有效地使用单线程应用程序。
答案 2 :(得分:5)
如果使用相同的锁,则锁定一个方法也会不必要地锁定其他方法。如果他们根本不相关,那么这是一个问题,因为他们必须等待彼此。他们不应该这样做。
当经常调用这些方法时,这可能会造成瓶颈。使用单独的锁定它们可以独立运行,但共享相同的锁定意味着它们必须等待锁定按需要更频繁地释放(实际上三次更频繁)。
答案 3 :(得分:2)
要创建线程安全的单例,请使用this technique 你不需要锁。
一般情况下,应尽可能少地使用每个锁 更多的方法锁定同一个东西,当你真的不需要时,你可能最终会等待它。
答案 4 :(得分:0)
好问题。使锁具有更细粒度而不是更粗粒度的优缺点,一个极端是针对每个数据段的单独锁,另一个极端是针对整个程序的一个锁。正如其他帖子所指出的那样,重用相同锁的缺点通常是您可能会获得较少的并发性(尽管取决于情况,您并不会获得较少的并发性。)
但是,使用更多锁的缺点通常是使死锁更有可能。涉及到的锁越多,死锁的方法就越多。例如,在单独的线程中同时获取两个锁,但以相反的顺序获取两个锁是潜在的死锁,如果只涉及一个锁,则不会发生。当然,有时您可以通过将一个锁分成两个来修复死锁,但是通常更少的锁意味着更少的死锁。还增加了具有更多锁的代码复杂性。
通常,这两个因素需要加以平衡。如果不引起任何并发问题,通常为每个类使用一个锁即可。实际上,这样做是一种称为监视器的设计模式。
我想说的最佳实践是,出于代码简单的考虑,倾向于减少锁,如果有充分的理由(例如并发,或者更简单或解决死锁的情况),则增加附加锁。