锁内锁


69

我想知道这种构造是否会导致错误:

lock(sync)
{
  // something
  lock(sync)
  {
    //something
    lock(sync)
    {
      //something
    }
  }
}

我已经运行了这段代码,看起来还不错,但是在某些情况下可能会引发错误?


为什么要使用这样的构造?
Jehof 2012年

您能描述一些需要这样做的情况吗?我正在尝试找出答案,但是..?
马蒂亚斯Fidemraizer

@MatíasFidemraizer递归函数可以锁定相同的资源。
ta.speot。是2012年

@ todda.speot.is啊...但是一把锁,一把锁里面,一把锁里面……很奇怪,不是吗?:D但感谢您的澄清!
马蒂亚斯Fidemraizer

35
这个问题非常有用...这是“ C#锁内锁”的第一个Google匹配。显然,代码不是像这样写的……内锁将在执行外锁的代码调用的方法中发生(通常不递归)。这是一种常见的模式,对任何有经验的程序员来说,问题的目的应该显而易见。
吉姆·巴尔特

Answers:


54

lock是的包装Monitor.EnterMonitor.Exit

lock关键字要求Enter在该块的开始和Exit在块的结尾。从前者的文档中:

从文档中Monitor.Enter

同一线程在Enter不阻塞的情况下多次调用是合法的。但是,Exit必须等待相等数量的调用,然后等待该对象的其他线程才能解除阻塞。

因为,在两个电话EnterExit配对,你的代码模式有明确定义的行为。

但是请注意,这lock不能保证是无异常的构造:

ThreadInterruptedException如果Interrupt中断正在等待输入lock语句的线程,则抛出A。


2
值得注意的是,内部locks本质上是多余的:您已经拥有锁;再次要求它永远不会失败。
Dan Puzey 2012年

14
确实不值得一提,因为持有锁的方法通常会调用获得锁的方法。在某些设计中,第二个锁将死锁...我已经在这样的系统上工作,程序必须保持自己的引用计数,并且仅在最外层获取和释放该锁。
Jim Balter 2013年

我同意吉姆。我只是有一个实例,在MVC应用程序中我需要一个内部锁。我选择使用相同的键在插入和更新处放置一个锁语句,然后在控制器中有一个我要锁定的区域,在该区域中关闭了报表。这将收集错误报告的数据,然后调用必要的DAL方法,某些方法被重用,并且可以由报告之外的其他进程调用。我想在关闭过程中冻结数据库的所有更改,因此错误报告是准确的,没有任何潜入。所以,是的,嵌套锁并不是多余的原因有很多。
eaglei22

16

要解释为什么它是定义良好的行为并且永远不会失败:

旁白:这个答案有更多关于锁如何实际工作的详细信息

锁定发生在该Thread级别,因此在同一线程上第二次调用将是多余的。我认为它不会有任何性能上的损失(尽管这将取决于.Net内部的精确编写方式,因此我不能保证)

很多时候,您会有一个公共函数来调用您的类中的另一个公共函数,这两个公共函数在单独使用时都需要锁。如果不允许这样做,则以下操作将失败:

private Dictionary<string, int> database = new Dictionary<string, int>();
private object databaseLock = new object();
public void AddOrUpdate(string item)
{
    lock (databaseLock)
    {
        if (Exists(item))
            database.Add(item, 1);
        else
            ++database[item];
    }
}
public bool Exists(string item)
{
    lock (databaseLock)
    {
        //... Maybe some pre-processing of the key or item...
        return database.ContainsKey(item);
    }
}

谢谢,我试图找出同一对象的锁内锁是否会起作用,正是因为一种方法调用了另一种方法。
mcmillab

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.