2011-01-30 116 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