我看到了以下代码,我想将它用于一个简单的活动,这个活动一次只能执行一次,并且不会频繁发生(因此一次发生两次的可能性非常小,但是你永远不知道。)
所以代码:
//class variable
private static object syncRoot = new object();
//in a method:
lock (syncRoot)
{
DoIt();
}
当另一个线程到来并想要执行代码时,它会等到锁被释放多长时间?永远,或者你能以某种方式设置超时?
和第二:如果DoIt()
方法抛出异常,锁是否仍然被释放?
答案 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)
答案 4 :(得分:1)
你应该后退一步,问问自己,为什么Monitor
Enter
Exit
DoIt()
DoIt()
try/catch
{{1}} {{1}} {{1}} {{1}}如果有{{1}}可能会抛出异常的机会(我会争辩说,如果可能的话,你重新编写{{1}}以便它不会),那么你应该有{{1}在lock()语句中阻塞,以便您可以确保执行任何必要的清理。
答案 5 :(得分:0)
答案 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);
});