Monitor.TryEnter / Monitor.Exit and SynchronizationLockExcep

本文关键字:Monitor SynchronizationLockExcep and TryEnter Exit | 更新日期: 2023-09-27 17:50:21

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

try
{
    try
    {
       if(!Monitor.TryEnter(obj, 2000)) 
       { 
            throw new Exception("can not lock"); 
       }
    }
    finally
    {
       Monitor.Exit(obj);
    }
}
catch
{
    //Log
}
上面的代码非常简化,实际上Enter和Exit语句位于自定义对象(锁管理器)中。

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

所以问题是,我怎么能知道如果线程谁使监视器。退出是执行Monitor.Enter的同一个线程?
我想我可以使用CurrentThread。我想同步进入和退出,但我不确定它是否足够"安全"。

Monitor.TryEnter / Monitor.Exit and SynchronizationLockExcep

所以问题是,我怎么能知道如果线程谁使监视器。退出是执行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);
    }
}

就像你认为的那样,把Monitor的调用。try-catch中的Exit是' duty '(dirty?),这里有一个非常简单的想法,试图'take the duty away'。同一个线程的锁是可重入的,如果一个线程获得成功,在它释放之前,来自另一个线程的尝试将失败。所以你可以考虑像这样:

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。退出两次,因为我们锁定了它两次,一次在代码外部,一次在这里。

我知道这是一个老问题,但这是我的答案。我会将try-finally结构移到if:

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