C#锁等待多长时间,如果代码在锁定期间崩溃怎么办?

时间:2011-05-18 18:28:57

标签: c# locking

我看到了以下代码,我想将它用于一个简单的活动,这个活动一次只能执行一次,并且不会频繁发生(因此一次发生两次的可能性非常小,但是你永远不知道。)

所以代码:

 //class variable
    private static object syncRoot = new object();

    //in a method:
    lock (syncRoot)
    {
     DoIt();
    }

当另一个线程到来并想要执行代码时,它会等到锁被释放多长时间?永远,或者你能以某种方式设置超时?

和第二:如果DoIt()方法抛出异常,锁是否仍然被释放?

7 个答案:

答案 0 :(得分:72)

  

当另一个线程到来并想要执行代码时,它会等到锁被释放多长时间?

lock将阻止尝试无限期进入锁的线程,直到被锁定的对象被释放为止。

  

你能以某种方式弄湿超时吗?

如果您需要指定超时,请使用

中的Monitor.TryEnter
if(Monitor.TryEnter(obj, new TimeSpan(0, 0, 1))) {
    try {
        body 
    }
    finally {
        Monitor.Exit(obj);
    }
}
  

如果DoIt()方法抛出异常,锁是否仍然被释放?

是的,lock(obj) { body }已翻译为

bool lockWasTaken = false;
var temp = obj;
try { Monitor.Enter(temp, ref lockWasTaken); { body } }
finally { if (lockWasTaken) Monitor.Exit(temp); }

有关抛出异常时可能发生的事情的详细信息,请参阅Locks and exceptions do not mix

答案 1 :(得分:31)

如上所述,常规锁将永远等待,这是一个死锁的风险。

首选机制是(并注意ref):

bool lockTaken = false;
try {
    Monitor.TryEnter(lockObj, timeout, ref lockTaken);
    if(!lockTaken) throw new TimeoutException(); // or compensate
    // work here...
} finally {
    if(lockTaken) Monitor.Exit(lockObj);
}

这避免了在某些边缘情况下不释放锁的风险。

finally(存在于任何合理的实现中)确保即使在错误条件下也会释放锁。

答案 2 :(得分:9)

一个简单的lock(syncRoot)将永远等待。

您可以将其替换为

if (System.Threading.Monitor.TryEnter(syncRoot, 1000))
{
     try
     {
         DoIt();
     }
     finally
     {
         System.Threading.Monitor.Exit(syncRoot);
     }
}

每个线程都应该确保异常安全锁定。

请注意,标准lock(syncRoot) {}会被重写为Monitor.Enter(syncRoot)和try / finally

答案 3 :(得分:3)

正如亨克所说,锁将永远等待。异常仍将解锁。它在内部使用try-finally块实现。

答案 4 :(得分:1)

你应该后退一步,问问自己,为什么Monitor Enter Exit DoIt() DoIt() try/catch {{1}} {{1}} {{1}} {{1}}如果有{{1}}可能会抛出异常的机会(我会争辩说,如果可能的话,你重新编写{{1}}以便它不会),那么你应该有{{1}在lock()语句中阻塞,以便您可以确保执行任何必要的清理。

答案 5 :(得分:0)

了解发生死锁的必要先决条件。始终使用Monitor vs Lock来避免死锁。

Conditions

答案 6 :(得分:-1)

Jason anwser非常出色,这是一种包装方式,使调用更简单。

    private void LockWithTimeout(object p_oLock, int p_iTimeout, Action p_aAction)
    {
        Exception eLockException = null;
        bool bLockWasTaken = false;
        try
        {
            Monitor.TryEnter(p_oLock, p_iTimeout, ref bLockWasTaken);
            if (bLockWasTaken)
                p_aAction();
            else
                throw new Exception("Timeout exceeded, unable to lock.");
        }
        catch (Exception ex)
        {
            // conserver l'exception
            eLockException = ex;
        }
        finally 
        { 
            // release le lock
            if (bLockWasTaken) 
                Monitor.Exit(p_oLock); 

            // relancer l'exception
            if (eLockException != null)
                throw new Exception("An exception occured during the lock proces.", eLockException);
        }
    }

然后,以这种方式使用它:

        // ajouter à la liste des fiches à loader
        LockWithTimeout(m_lLoadingQueue, 3600, () =>
        {
            m_lLoadingQueue.Add(p_efEcranFiche);
        });