Monitor.TryEnter / Monitor.Exit和SynchronizationLockException
是否有可能检测到同一个线程是否试图释放锁定? 我们在代码中有很多地方看起来像:
try { try { if(!Monitor.TryEnter(obj, 2000)) { throw new Exception("can not lock"); } } finally { Monitor.Exit(obj); } } catch { //Log }
上面的代码非常简化,实际上Enter和Exit语句位于自定义对象(lock manager)中。
问题是,在该结构中,我们在尝试“退出”时有SynchronizationLockException
,因为它看起来像是没有成功锁定的线程,试图最终释放。
所以问题是,我怎么知道创建Monitor.Exit的线程是否与Monitor.Enter的线程相同?
我以为我可以使用CurrentThread.Id同步进入和退出,但我不确定它是否“足够安全”。
正如你认为在try-catch中调用Monitor.Exit是’durty’(脏?),这是一个非常简单的想法,试图“把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两次,因为我们将它锁定两次,一次在代码外部,一次在这里。
所以问题是,我怎么知道创建Monitor.Exit的线程是否与Monitor.Enter的线程相同?
就我所知,你不能轻易做到。 您无法找到哪个线程拥有监视器。
但是,这只是一个编码问题 – 您应该更改代码,以便它甚至不会尝试在不应该的情况下释放监视器。 所以你上面的代码可以重写为:
if (!Monitor.TryEnter(obj, 2000)) { throw new Exception(...); } try { // Presumably other code } finally { Monitor.Exit(obj); }
或者甚至更好,如果您使用的是.NET 4,请使用TryEnter
的重载,它接受一个ret
参数:
bool gotMonitor = false; try { Monitor.TryEnter(obj, ref gotMonitor); if (!gotMonitor) { throw new Exception(...); } // Presumably other code } finally { if (gotMonitor) { Monitor.Exit(obj); } }
我知道这是一个较老的问题,但无论如何,这是我的答案。 我会在if中移动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 }