我应该为线程锁定一部分在c#中调用线程安全方法的代码吗?
例如:
T Eject (bool? state)
{
lock (this)
{
//somecode
return _objects[i];
}
}
public T TakeNew()
{
return Eject(null);
}
public T Reserve()
{
return Eject(true);
}
或者我应该像这样锁定回报:
lock(this)
{
return Eject(true);
}
如果我锁定所有调用Eject(true),我是否需要锁定方法块的弹出?我想,如果我锁定方法和调用方法,它会创建一个线程死锁。在这种情况下,创建线程安全代码的更好方法是什么?
感谢您的回答!
答案 0 :(得分:1)
最佳做法是尽可能少地锁定代码,因为这样可以最大限度地减少锁争用的可能性(从而提高性能和可伸缩性),还可以提高代码的可读性(更容易看到哪些对象受锁保护)。 / p>
在您的情况下,仅锁定_objects
方法内的Eject
访问权限就足够了(请参阅下面的代码)。因此不需要锁定方法调用return Eject(true)
(但如果这样做,则不会导致死锁)。
使用this
进行锁定也被视为不良做法,因为这可能会在某些条件下导致死锁。因此,专用对象通常存储在封闭类的私有或受保护字段中并用于锁定(再次参见下面的代码)。
所以这应该没问题:
class Foo<T>
{
// Dedicated object instance used for locking
private Object m_MyLock = new Object();
T Eject(bool? state)
{
lock (m_MyLock)//You typically use the same locking object everywhere within the class
{
//somecode
return _objects[i];
}
}
string SomeOtherMethod()//Some other method that needs to be threadsafe too
{
lock (m_MyLock)//You typically use the same locking object everywhere within the class
{
return _objects[i].ToString();
}
}
public T TakeNew()
{
return Eject(null);
}
public T Reserve()
{
return Eject(true);
}
}
澄清OP的评论:你不应该为每个锁创建新的锁定对象。您通常在类中使用相同的锁定对象。当你在你的班级中使用lock(this)
而另一部分代码没有意识到(例如第三方库代码)使用你班级的实例时,我说的死锁情况可能会发生以这种不幸的方式锁定对象,导致死锁。在Why is lock(this) {…} bad?中更好地解释了这种情况(包括这种情况的例子)。这就是为什么你应该使用私有对象(m_MyLock
)进行锁定的原因,因为这样的情况不会发生(尽管它不排除其他可能的死锁情况)。