Monitor.TryEnter/Monitor.Exit和SynchronizationLockException

Ale*_* Dn 4 .net c# multithreading locking

是否有可能检测到同一个线程是否试图释放锁定?我们在代码中有很多地方看起来像:

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

上面的代码非常简化,实际上Enter和Exit语句位于自定义对象(lock manager)中.

问题是,在那个结构中,我们SynchronizationLockException在尝试"退出"时有,因为它看起来像是没有成功锁定的线程,试图最终释放.

所以问题是,我怎么知道创建Monitor.Exit的线程是否与Monitor.Enter的线程相同?
我以为我可以使用CurrentThread.Id同步进入和退出,但我不确定它是否"足够安全".

Jon*_*eet 16

所以问题是,我怎么知道创建Monitor.Exit的线程是否与Monitor.Enter的线程相同?

就我所知,你不能轻易搞定.您无法找到哪个线程拥有监视器.

但是,这只是一个编码问题 - 您应该更改代码,以便它甚至不会尝试在不应该的情况下释放监视器.因此,您上面的代码可以重写为:

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

或者甚至更好,如果您使用的是.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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @AlexDn:我仍然说你基本上是在解决损坏的代码。这就像捕获 `NullReferenceException` 而不是修复您的代码以避免取消引用空值 - 是的,它有效,但“修复”代码比在上面贴上石膏更好。 (2认同)

Ken*_*Kin 2

当您认为将 Monitor.Exit 的调用放在 try-catch 中是“肮脏的”(肮脏的?)时,这里有一个非常简单的想法,试图“拿走这种肮脏”。锁对于同一线程是可重入的,如果一个线程成功获取锁,则在释放锁之前,另一线程的尝试将失败。这样你就可以考虑这样的事情:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我们调用 Monitor.Exit 两次,因为我们锁定了它两次,一次在代码外部,一次就在这里。

  • 如果“LockDictionary”在这里是一个“Dictionary<,>”,则需要注意它不是线程安全的。我仍然认为修复代码而不是解决其损坏会是一个更好的主意。 (2认同)