如何在IList上使用锁

时间:2009-08-24 02:18:02

标签: c# multithreading

我有一个实现IList接口的静态集合。此集合在整个应用程序中使用,包括添加/删除项目。

由于多线程问题,我想知道我能做些什么来确保列表一次修改一个,例如当1个线程尝试添加项目时,另一个线程当时不应该删除项目。

我想知道lock(this)和lock(privateObject)之间有什么区别?在我的情况下哪一个更好?

谢谢。

3 个答案:

答案 0 :(得分:4)

lock(this)将锁定整个实例,而lock(privateObject)将仅锁定该特定实例变量。第二个是更好的选择,因为锁定整个实例将阻止其他线程对该对象做任何事情。

来自MSDN

  

一般情况下,请避免锁定公开   类型或代码之外的实例   控制。常见的构造锁定   (this),lock(typeof(MyType))和   锁(“myLock”)违反此规定   准则:

     
      
  • 锁定(这个)是一个问题,如果   实例可以公开访问。

  •   
  • lock(typeof(MyType))是一个问题,如果   MyType可公开访问。

  •   
  • lock(“myLock”)是一个问题,因为任何问题   使用过程中的其他代码   相同的字符串,将共享相同的锁。

  •   
     

最佳做法是定义私有   要锁定的对象,或私有静态   对象变量保护数据常见   对所有情况。

在这种特殊情况下,集合是静态的,这实际上意味着只有一个实例,但仍然不会改变lock(this)lock(privateObject)的行为方式。

即使在静态集合中使用lock(this),您仍然可以锁定整个实例。在这种情况下,只要线程A获得方法Foo()的锁定,所有其他线程就必须等待对集合执行任何操作。

使用lock(privateObject)意味着一旦线程A获得方法Foo()的锁定,所有其他线程就可以执行除Foo()之外的任何其他操作而无需等待。只有当另一个线程尝试执行方法Foo()时,它才必须等到线程A完成其Foo()操作并释放锁定。

答案 1 :(得分:1)

lock关键字有点令人困惑。 lock语句中的对象表达式实际上只是用于创建关键部分的识别机制。它不是锁的主题,也不是因为它被声明引用而以任何方式保证对多线程操作是安全的。

因此lock(this)正在创建由包含当前正在执行的方法的类标识的关键部分,而lock(privateObject)由对象(可能是无论如何)私有的类标识。前者风险更大,因为类的调用者可能无意中使用使用该类实例作为锁定对象的lock语句来定义自己的关键部分。这可能会导致无意识的线程问题,包括但不限于死锁和瓶颈。

您提到您关注的是同时修改集合的多个线程。我应该指出,即使他们没有修改它,你也应该同样关注读取集合的线程。您可能需要一些相同的安全防护装置来保护读取过程中的集合,就像在写入期间一样。

答案 2 :(得分:1)

将私有成员添加到方法锁定的类中。

例如

public class MyClass : IList
{
    private object syncRoot = new object();

    public void Add(object value)
    {
        lock(this.syncRoot)
        {
           // Add code here
        }
    }

    public void Remove(object value)
    {
        lock(this.syncRoot)
        {
            // Remove code here
        }
    }
}

这将确保在线程之间同步访问列表以添加和删除案例,同时保持对列表的访问。这仍然允许枚举器访问列表,而另一个线程可以修改它,但这会打开另一个问题,如果在枚举期间修改了集合,枚举器将抛出异常。