Monitor.TryEnter / Monitor.Exit和SynchronizationLockException

时间:2012-12-30 09:22:04

标签: c# .net multithreading locking

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

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同步进入和退出,但我不确定它是否“足够安全”。

3 个答案:

答案 0 :(得分:16)

  

所以问题是,我怎么知道创建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);
    }
}

答案 1 :(得分:2)

正如你认为在try-catch中调用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,因为我们将它锁定两次,一次在代码外部,一次在此处。

答案 2 :(得分:0)

我知道这是一个较老的问题,但无论如何,这是我的答案。 我会在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
}