2016-04-19 46 views
0

使用Monitor.Enter和Monitor.Exit時,我似乎遇到併發問題。有時候,我的代碼掛在下面的Monitor.Exit聲明:Monitor.Exit()有時會掛起

public void EndInit() 
{ 
    Monitor.Enter(this.lockObj); 
    this.initCount--; 
    if (this.initCount == 0) { 
     this.IsInitializing = false; 
     this.IsInitialized = true; 
     this.OnInitialized(); 
    } 
    // sometimes, this Exit will never return ... 
    Monitor.Exit(this.lockObj); 
} 

這裏只有一個其他地方,在那裏我lockObj用於:

public void BeginInit() 
{ 
    Monitor.Enter(this.lockObj); 
    this.initCount++; 
    this.IsInitializing = true; 
    this.IsInitialized = false; 
    Monitor.Exit(this.lockObj); 
} 

這就是我如何聲明此同步對象:

private readonly object lockObj = new object(); 

我正在撕掉我的頭髮,發現它在這裏發生了什麼,但沒有成功。我期望Monitor.Enter()阻止,直到我的同步對象被釋放,但爲什麼Monitor.Exit()被阻止?我無法在MSDN中找到有關此行爲的任何解釋。

注意我不能重現這種行爲,它發生的很隨機(嗯,我知道「隨機」不是正確的措辭)。

任何想法或有用的提示,高度讚賞!

Thorsten

+1

如果可能,您可以用'lock(){}'塊代替'Monitor.Enter' /'Monitor.Exit'組合。如果在Monitor.Exit調用之前拋出異常會發生什麼? – Dbuggy

回答

3

我正在回答我以前的評論。因爲在OnInitialize()中發生異常時,應該使用try finally構造來正確地調用Monitor.Exit

因此,代碼會隨即成爲

public void EndInit() 
{ 
    Monitor.Enter(this.lockObj); 
    try 
    { 
     this.initCount--; 
     if (this.initCount == 0) { 
      this.IsInitializing = false; 
      this.IsInitialized = true; 
      this.OnInitialized(); 
     } 
    } 
    finally 
    { 
     Monitor.Exit(this.lockObj);  
    } 
} 

這同樣適用於第二種方法。

它也可以寫成這樣便

public void EndInit() 
{ 
    lock(this.lockObj) 
    { 
     this.initCount--; 
     if (this.initCount == 0) { 
      this.IsInitializing = false; 
      this.IsInitialized = true; 
      this.OnInitialized(); 
     } 
    } 
} 

編輯

穿線寫喬阿爾巴哈利很好的解釋可以發現here。這是一本值得閱讀的文章。

編輯2(用於完整性)

同樣如由Damien_The_Unbeliever指出,有一個overload

這隻適用於.NET 4及更高版本。使用監視器的代碼將變爲:

public void EndInit() 
{ 
    bool lockAcuired = false; 
    try 
    { 
     Monitor.Enter(this.lockObj, ref lockAquired); 
     this.initCount--; 
     if (this.initCount == 0) { 
      this.IsInitializing = false; 
      this.IsInitialized = true; 
      this.OnInitialized(); 
     } 
    } 
    finally 
    { 
     if(lockAquired) 
      Monitor.Exit(this.lockObj);  
    } 
} 
+2

如果您要手動實現'lock',則應該使用使用兩個參數['Enter'](https://msdn.microsoft.com/zh-cn/library/dd289498(v = vs.110).aspx)*封閉*在'try'塊中。它是由...引入的。NET 4(我相信)處理在返回的'Enter'和'try'塊之間拋出異常的邊緣情況(在當前變體中,沒有調用'Exit') –

+0

寫入這個問題和深入挖掘我自己的(舊)代碼,我只是想出了這個問題的相同(潛在)來源:'OnInitialized()'可能會拋出一個異常,目前未處理(甚至無法識別)。我將這個調用從同步塊中移出並添加了'try' /'catch'。未來將顯示,如果這可以解決問題。 – ThorstenHa

+0

@Damien_The_Unbeliever我更新了我對.NET4版本的回答。感謝您指出了這一點。 – Dbuggy