在两种不同的场景中锁定性能

时间:2010-12-23 11:22:03

标签: c# multithreading locking thread-safety

我有一个包含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
           }
       }
    }

3 个答案:

答案 0 :(得分:3)

这里有两个不同的问题:

  • 是使用一个组合锁还是两个单独的锁
  • 是否使用单独的对象锁定或列表本身(或其SyncRoot s)

这些在某种程度上是可分离的 - 因为如果你使用两个单独的锁,你可以创建两个单独的对象来锁定,每个列表一个。

如果您希望MethodThatUsesAListMethodThatUsesBList能够同时运行,则需要两个单独的锁。然后,您必须确保无论何时获得两个锁,您都可以按相同的顺序获取它们。这意味着推理任何获取锁的方法中的所有代码:例如,你需要确保它不会调用另一个获取另一个锁的方法。

如果您的特定方案不太可能受到 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)

总的来说,我相信前者(另一种未使用的互斥体)是首选。这样做没有实际成本,太容易让后者变得错误,太容易造成死锁。