在一种方法中锁定某些对象也使其锁定在另一种方法中

本文关键字:锁定 方法 对象 另一种 一种 | 更新日期: 2023-09-27 18:36:09

当某个线程锁定myList SomeMethodA并在lock内执行块时,其他线程是否可以在SomeMethodB中执行myList.Add(1),或者它会等待,因为"myList"被锁定在SomeMethodA中?

class A
{
    private List<int> myList;
    public void SomeMethodA()
    {
       lock(myList)
       {
          //...
       }
    }
    public void SomeMethodB()
    {
       myList.Add(1);
    }
}

在一种方法中锁定某些对象也使其锁定在另一种方法中

编辑 明确答案:不,您需要在SomeMethodB中明确锁定列表。编译器不会自动为您添加锁

  • 否则,您为什么必须显式锁定?
  • 事情会非常缓慢。只禁止多线程比总是锁定每个对象访问要好得多1

推荐的成语是这样的:

class A
{
    private List<int> myList;
    private readonly object _lockObject = new Object();
    public void SomeMethodA()
    {
       lock(_lockObject)
       {
          //...
       }
    }
    public void SomeMethodB()
    {
       lock(_lockObject)
       {
           myList.Add(1);
       }
    }
}

当心像这样暴露细粒度锁定(只要锁下不发生阻塞操作,您通常希望执行粗粒度锁定)。

注意 但是,C# 中的锁是可重入的,因此从锁中的SomeMethodA中调用SomeMethodB不会死锁

更新使用私有锁object实例背后的基本原理:

通常,请避免锁定公共类型或代码无法控制的实例。常见的构造 lock (this)、lock (typeof (MyType)) 和 lock ("myLock") 违反了以下准则:

  • 如果可以公开访问实例,则lock (this)是一个问题。
  • 如果MyType可公开访问,则lock (typeof (MyType))是一个问题。
  • lock("myLock")是一个问题,因为进程中使用相同字符串的任何其他代码都将共享相同的锁。

最佳做法是定义要锁定的私有对象,或定义私有静态对象变量以保护所有实例通用的数据。

请参阅:http://msdn.microsoft.com/en-us/library/c5kehkcz.aspx


1(除了该方法的其他问题,例如空值、引用更新、死锁等)

锁定对象会在应用程序中全局锁定对象。您甚至可以观察其他类中的锁定(如果您锁定了公共对象)。

但是,在您的示例代码中,myList.Add(1)不会等待锁,因为您尚未将其包装在 lock 块中。

当我们说"锁定对象"

或"获取对象锁定"时,这有点用词不当,因为lock语句实际上与阻止访问对象没有任何关系。相反,它通过使用该对象作为"键"来防止多个线程进入锁定的代码块 - 并且一次只有一个线程可以拥有该键。因此,当您"锁定"一个对象时,所有线程仍然可以自由使用该对象,但一次只有一个线程可以使用该对象进入lock块。

答案是否定的。一旦一个线程获得了某个对象的锁(在您的情况下myList),没有其他线程可以访问该对象的锁,Thread1 将阻止所有其他线程,直到 Thread1 释放myList锁,但条件是如果其他线程也在尝试锁定。

在您的示例中,如果 Thread1 正在执行SomeMethodA()(其锁为 myList),并且 Thread2 正在执行SomeMethodB()(它不请求锁定),则不会有任何问题,它们不会相互阻塞

请考虑以下示例以获得更多说明。

class A
{
    private List<int> myList;
    public void SomeMethodA()
    {
       lock (myList)
       {
           //...
       }
    }
    public void SomeMethodB()
    {
        myList.Add(1);
    }
    public void SomeMethodC()
    {
        lock (myList)
        {
            myList.Add(2);
        }
    }
}

线程 1 正在尝试访问SomeMethodA()

线程 2 正在尝试访问SomeMethodB()

线程 3 正在尝试访问SomeMethodC()

线程

1 和线程 2 将在不相互阻塞的情况下执行,但线程 3 将在myList锁定时被阻止,因为这已被线程 1 获取。

如果你没有在SomeMethodB里面放一个锁,所有线程都可以访问它,即使你有一个线程在SomeMethodA的锁中执行。为了防止线程运行SomeMethodB你需要像sehe一样实现你的方法。