我有一个包含2个内联列表的类,其中包含超过1000个这样的元素,
public class A
{
private List<long> aList = new List<long>();
private List<long> bList = new List<long>();
void MethodThatUsesAList()
{
//Works with aList
}
void MethodThatUsesBList()
{
//Works with bList
}
void MethodThatUsesBoth()
{
//Works with both lists
}
}
现在,我想让这个类线程安全,我看到有2种不同的场景,我首先使用锁定,首先创建
object _synchObject = new object()
并在所有方法中锁定此对象, 或者在使用时锁定每个列表, 我的第一个问题是,首选哪种方法? 并且在第二种情况下是首选的,这样使用锁是否存在性能问题?
void MethodThatUsesBoth()
{
lock(aList){
lock(bList){
//Works with both lists
}
}
}
答案 0 :(得分:3)
这里有两个不同的问题:
SyncRoot
s)这些在某种程度上是可分离的 - 因为如果你使用两个单独的锁,你可以创建两个单独的对象来锁定,每个列表一个。
如果您希望MethodThatUsesAList
和MethodThatUsesBList
能够同时运行,则需要两个单独的锁。然后,您必须确保无论何时获得两个锁,您都可以按相同的顺序获取它们。这意味着推理任何获取锁的方法中的所有代码:例如,你需要确保它不会调用另一个获取另一个锁的方法。
如果您的特定方案不太可能受到 all 的影响,那么任何一个运行任何的线程的其他线程都会阻止这些方法,那么我就是d为了简单起见,使用单个锁。
在任何一种情况下,我都会亲自去寻找其他代码所知道的“私人”锁。我发现用这种方式编写的代码更容易理由。使用列表本身或同步根可能是绝对正常的 - 但我更喜欢考虑没有其他任何东西可能获得的锁。
答案 1 :(得分:1)
void MethodThatUsesBoth()
{
lock(((ICollection)aList).SyncRoot){
lock(((ICollection)bList).SyncRoot){
//Works with both lists
}
}
}
void MethodThatUsesAList()
{
lock(((ICollection)aList).SyncRoot){
}
}
void MethodThatUsesBList()
{
lock(((ICollection)bList).SyncRoot){
}
}
在您的其他解决方案中,我认为这不是很好,因为当您仅使用aList时阻止对bList的访问。这不是一个好的性能解决方案。
答案 2 :(得分:0)
总的来说,我相信前者(另一种未使用的互斥体)是首选。这样做没有实际成本,太容易让后者变得错误,太容易造成死锁。