2012-12-30 58 views
3

是否可以檢測到嘗試釋放鎖的同一個線程? 我們在代碼,看起來像很多地方:Monitor.TryEnter/Monitor.Exit和SynchronizationLockException

try 
{ 
    try 
    { 
     if(!Monitor.TryEnter(obj, 2000)) 
     { 
      throw new Exception("can not lock"); 
     } 
    } 
    finally 
    { 
     Monitor.Exit(obj); 
    } 
} 
catch 
{ 
    //Log 
} 

上面的代碼非常簡單,實際上進入和退出聲明中位於自定義對象(鎖管理器)。

問題是,在那個結構中,我們有SynchronizationLockException在試圖「退出」時,因爲它看起來像鎖定不成功的線程,最後試圖釋放。

所以問題是,我如何知道是否使Monitor.Exit的線程是Monitor.Enter相同的線程?
我以爲我可以使用CurrentThread.Id來同步進入和退出,但我不確定它是否足夠「安全」。

+0

既然你聲稱,實際上進入和退出聲明中分別位於自定義對象,我想知道你是否能在訪問'obj' outter塊? –

+0

不,在外部塊中,我沒有引用對象,因爲鎖管理器接收到「key」作爲參數並創建它自己的鎖對象。你可以在我之前的問題中看到我的鎖管理器:http://stackoverflow.com/questions/14030491/net-multithreading-lock-object-with-log-when-locked-for-to-much-time –

回答

1

當你認爲到試圖抓住Monitor.Exit的呼叫是'durty'(骯髒的?),這裏有一個非常簡單的想法,試圖「走開」。對於同一線程,鎖定是可重入的,並且如果一個線程成功獲取,在它釋放之前,嘗試從另一個線程將失敗。所以,你可以考慮這樣的:

public void Exit(object key) { 
    if(!IsActive) { 
     return; 
    } 

    if(LockDictionary.ContainsKey(key)) { 
     var syncObject=LockDictionary[key]; 

     if(Monitor.TryEnter(syncObject.SyncObject, 0)) { 
      SetLockExit(syncObject); 
      Monitor.Exit(syncObject.SyncObject); 
      Monitor.Exit(syncObject.SyncObject); 
     } 
    } 
} 

我們稱之爲Monitor.Exit兩次,因爲我們鎖定了兩次,一次在代碼外,另一個就在這裏。

+1

嗯......非常漂亮的「乾淨」解決方案:)它看起來非常簡單,喜歡它應該適合我的情況!非常感謝你!順便說一句,'durty'是'髒' - 對我難看的英語感到抱歉:)) –

+2

如果'LockDictionary'在這裏是'Dictionary <,>',那麼你需要知道它不是線程安全的。我仍然認爲修復代碼而不是解決它的破壞會是一個更好的主意。 –

+0

@Ken Kin,你能解釋一下,爲什麼第一次TryEnter失敗的線程第二次進入鎖定不可能?在開始執行finally塊時,另一個線程已經可以離開它了。 – mistika

14

所以問題是,我怎麼知道是否使Monitor.Exit的線程誰是Monitor.Enter相同的線程?

就我所知,你無法輕易達成目標。您無法找出哪個線程擁有顯示器。

然而,這僅僅是一個編碼問題 - 這樣,它甚至不嘗試釋放監視器時它不應該,你應該改變你的代碼。所以,上面的代碼可以被改寫爲:

if (!Monitor.TryEnter(obj, 2000)) 
{ 
    throw new Exception(...); 
} 
try 
{ 
    // Presumably other code 
} 
finally 
{ 
    Monitor.Exit(obj); 
} 

甚至更​​好,如果你使用.NET 4中,使用的接受一個ret參數overload of TryEnter

bool gotMonitor = false; 
try 
{ 
    Monitor.TryEnter(obj, ref gotMonitor); 
    if (!gotMonitor) 
    { 
     throw new Exception(...); 
    } 
    // Presumably other code 
} 
finally 
{ 
    if (gotMonitor) 
    { 
     Monitor.Exit(obj); 
    } 
} 
+0

根據帖子,似乎內部嘗試終於不打算修改。 –

+0

@KenKin,是的,try-catch不應該被修改,但它看起來不可能實現我在當前代碼結構中需要的東西。我想過了,但仍希望解決它:) Jon,感謝您的明確回答,現在我們需要決定是否移除TryEnter的使用並使用Enter,或者我們需要重構大量的代碼。 –

+1

@KenKin:鑑於它被破壞*,我認爲這是合理的說它基本上必須被修改... –

0

我知道這是一個較老的問題,但這裏是我的答案。 我會移動的try-finally結構內,如果:

try 
{ 
    if(Monitor.TryEnter(obj, 2000)) 
    { 
     try 
     { 
      // code here 
     } 
     finally 
     { 
      Monitor.Exit(obj); 
     } 
    } 
    else 
    { 
     throw new Exception("Can't acquire lock"); 
    } 
} 
catch 
{ 
    // log 
}