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

Mic*_*hel 47 c# locking

我看到下面的代码,我想用它来做一个简单的活动,这个活动一次只能执行一次,并且不会经常发生(因此一次发生两次的机会非常小,但你永远不知道).

所以代码:

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

// in a method:
lock (syncRoot)
{
    DoIt();
}
Run Code Online (Sandbox Code Playgroud)

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

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

jas*_*son 76

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

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

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

如果需要指定超时,请使用Monitor.TryEnterin

if(Monitor.TryEnter(obj, new TimeSpan(0, 0, 1))) {
    try {
        body 
    }
    finally {
        Monitor.Exit(obj);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

是的,a DoIt()被翻译成

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

有关抛出异常时可能发生的事情的详细信息,请参阅锁定和异常不混合.

  • **一个公平的警告!**using语句不是100%安全的(很多人认为它是!):using语句只会调用Dispose _if它收到了IDisposable-reference_.这种情况很快发生在使用括号内的语句后,即`using(/*this stuff*/)`完成.由于绝对不需要原子性(该语句可以任意长),可能会发生一些事情,阻止(编译器生成的)使用代码接收您聪明的IDisposable引用._(tl; dr:将using-statements简单化 - 将复杂的东西放入其体内.)_ (5认同)

Mar*_*ell 33

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

首选机制是(并注意到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);
}
Run Code Online (Sandbox Code Playgroud)

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

finally(其存在于任何合理的实现),确保锁即使在错误条件释放.

  • 我想补充一点,很多程序员将finally语句添加为_习惯_,但它不应该是一个习惯:“在错误条件下释放锁”并不自动神奇地比死锁更好。这是应该根据具体情况进行评估的事情。在安全关键的场景中,在错误状态下释放锁可能很危险 - 检测死锁进程并执行某种恢复过程有时比释放锁更安全。“有时”是关键词。 (2认同)

Hen*_*man 9

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

你可以用它替换它

if (System.Threading.Monitor.TryEnter(syncRoot, 1000))
{
     try
     {
         DoIt();
     }
     finally
     {
         System.Threading.Monitor.Exit(syncRoot);
     }
}
Run Code Online (Sandbox Code Playgroud)

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

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