2011-01-30 148 views
20

我已经写了一个我认为应该成为僵局的有效案例的测试。看起来,一旦lock已被某个类的实例获取,那么即使我明确尝试再次调用另一个应该返回lock的方法,该实例也不需要重新获取lock锁定已锁定并进一步尝试锁定不会阻止:是否C#锁可重入?

这里是类:

internal class Tester 
{ 
    private readonly object _sync = new object(); 

    public Tester() { } 

    public void TestLock() 
    { 
     lock (_sync) 
     { 
      for (int i = 0; i < 10; i++) 
      { 
       Deadlock(i); 
      } 
     } 

    } 

    private void Deadlock(int i) 
    { 
     lock (_sync) 
     { 
      Trace.WriteLine(i + " no deadlock!"); 
     } 
    } 
} 

输出:

0无僵局!
1没有死锁!
2没有死锁!
3没有死锁!
4没有死锁!
5没有死锁!
6没有死锁!
7没有死锁!
8没有死锁!
9没有死锁!

我原以为这会造成死锁......任何人都可以对此有所了解吗?

回答

41

.NET中的锁定是可重入的。只有来自其他线程的收购被阻止。当同一个线程多次锁定同一个对象时,它只是增加一个计数器,并在释放时递减。当计数器达到零时,锁定从实际上释放为从其他线程访问。

1

在您的方案中,您在另一个锁内有锁。一旦代码在“死锁”中碰到嵌套锁,“锁(...)”代码基本上被忽略,因为它已经在“TestLock”中获得了它。

线程的重要来源:http://www.albahari.com/threading/part2.aspx

+1

我对多线程非常舒服,但我想我只是从来没有意识到C#锁是可重入的。感谢您的回答... – Kiril 2011-01-30 22:39:36

13

Monitor,Mutex和ReaderWriterLock类维护具有线程关联性的锁。 ReaderWriterLockSlim类让你选择,它有一个构造函数,它接受一个LockRecursionPolicy值。使用LockRecursionPolicy.NoRecursion是一种优化,如果你的锁定真的很细致,那么这是一个相当大的优化。

Semaphore类是一个同步类,它没有任何线程关联。此代码可靠地死锁:

class Tester { 
    private Semaphore sem = new Semaphore(1, 1); 
    public void TestLock() { 
     sem.WaitOne(); 
     for (int i = 0; i < 10; i++) Deadlock(i); 
     sem.Release(); 
    } 

    private void Deadlock(int i) { 
     if (!sem.WaitOne(100)) Console.WriteLine("deadlock!"); 
     else { 
      sem.Release(); 
      Console.WriteLine("No deadlock!"); 
     } 
    } 
} 

通常,线程仿射同步类需要两个线程和两个锁来锁死。标准模式是为一个线程获取锁A和B,另一个线程获取B和A.顺序很重要。

在.NET编程中存在一些不太明显的死锁情况,这些情况是由于它们内置于.NET框架代码而无法看到的。一个非常经典的是BackgroundWorker。您可以在UI线程中编写代码,使其在Busy属性上旋转,等待BGW完成。当BGW有一个RunWorkerCompleted事件处理程序时,这总是会发生死锁。它只有在UI线程空闲时才能运行,直到事件处理程序运行完毕,BGW的Busy属性才会失效。

+0

感谢您的好信息! – Kiril 2011-01-30 23:56:51