C#中的重入锁


119

下面的代码是否会在.NET上使用C#导致死锁?

 class MyClass
 {
    private object lockObj = new object();

    public void Foo()
    {
        lock(lockObj)
        { 
             Bar();
        }
    }

    public void Bar()
    {
        lock(lockObj)
        { 
          // Do something 
        }
    }       
 }

6
我们可能会考虑更改此问题的标题-也许更改为最近关闭的内容,为什么嵌套锁不会导致死锁?就目前而言,该标题似乎几乎是为了防止人们发现它而设计的。
杰夫·斯特恩

12
实际上,我是根据搜索词“可重入”发现的,它回答了我的问题。如果这是一个重复的问题,那是一个不同的问题……
emfurry 2011年

我同意@JeffSternal的评论,此问题假定搜索该问题的人员已经熟悉“重入”锁。:另一个问题重复我觉得有这个一个好的标题stackoverflow.com/questions/3687505/...
路易斯·佩雷斯

Answers:


148

不,只要您锁定在同一对象上即可。递归代码实际上已经具有锁定,因此可以不受阻碍地继续进行。

lock(object) {...}是使用Monitor类的简写。正如Marc指出的那样Monitor允许重新进入,因此反复尝试锁定当前线程已具有锁定状态的对象将很好。

如果您开始锁定其他对象,则必须小心。请特别注意:

  • 始终以相同的顺序获取给定数量的对象上的锁。
  • 始终以与获取锁相反的顺序释放锁。

如果您违反了这两条规则的话,一定会遇到死锁问题。

这是一个描述.NET中线程同步的好网页:http : //dotnetdebug.ne​​t/2005/07/20/monitor-class-avoiding-deadlocks/

同样,一次锁定尽可能少的对象。考虑在可能的情况下应用粗粒度的锁。这样的想法是,如果您可以编写代码以使有一个对象图,并且可以在该对象图的根上获取锁,则可以这样做。这意味着您在该根对象上拥有一个锁,因此不必太担心获取/释放锁的顺序。

(还要注意,您的示例在技术上不是递归的。要使其具有递归性Bar(),通常必须在迭代过程中调用自身。)


1
尤其是以不同的顺序。
Marc Gravell

6
递归;确实; 为了盖伊的利益,该词可重入
马克·格雷夫

感谢您对术语的澄清-我已经编辑并更正了该问题。
盖伊

这个问题似乎引起了很多关注,因此,我用自从我第一次写它以来就提出的其他一些注意事项更新了答案。
尼尔·巴恩威尔

实际上,我认为释放锁的顺序并不重要。选择它们的顺序肯定是这样,但是只要释放锁不以任何条件为条件(可以随时释放),只要释放所有已获得的锁,就可以了。
bobroxsox

20

好吧,Monitor允许重新进入,所以你不能死锁自己……所以不:它不应该这样做


7

如果线程已经持有锁,那么它将不会自身阻塞。.Net框架确保了这一点。您只需要确保两个线程都不会通过任何代码路径尝试不按顺序获取相同的两个锁。

同一线程可以多次获取同一锁,但是必须确保释放锁的次数与获取锁的次数相同。当然,只要您使用“ lock”关键字来完成此操作,它就会自动发生。


请注意,这对于监视器是正确的,但不一定是其他类型的锁。
乔恩·斯基特

(当然,这并不意味着您不知道这是一个重要的区别:)
Jon Skeet

那是个很好的观点。实际上,我本来打算将“锁定”更改为“显示器”,但后来我分心了。懒 Windows互斥锁内核对象的行为也是如此,所以我想,足够接近了!
Jeffrey L Whitledge,

5

不,此代码不会有死锁。如果您真的想创建死锁,则最简单的死锁至少需要2个资源。考虑狗和骨头的情况。一只狗对一根骨头有完全的控制权,所以其他任何一只狗都必须等待。2.当两只狗分别锁定自己的骨头并寻找其他骨头时,最少需要2条带有2条骨头的狗来产生死锁。

..依此类推,n条狗和多条骨头,导致更复杂的死锁。

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.