如何获得两个地方的锁定,但在一个地方释放?

时间:2011-09-27 19:45:32

标签: c# concurrency locking

我是c#的新手。我需要在两种方法中获得锁定,但是在一种方法中释放。那会有用吗?

public void obtainLock() {
    Monitor.Enter(lockObj);
}

public void obtainReleaseLock() {
    lock (lockObj) {
        doStuff
    }
}

特别是我可以拨打obtainLock然后再拨打obtainReleaseLock吗? C#中是否允许“doubleLock”?始终从同一个线程调用这两个方法,但在另一个线程中使用lockObj进行同步。

更新:在所有评论之后你对这些代码有什么看法?这是理想的吗?

public void obtainLock() {
    if (needCallMonitorExit == false) {
        Monitor.Enter(lockObj);
        needCallMonitorExit = true;
    }
    // doStuff
}

public void obtainReleaseLock() {
    try {
        lock (lockObj) {
            // doAnotherStuff
        }
    } finally {
        if (needCallMonitorExit == true) {
            needCallMonitorExit = false;
            Monitor.Exit(lockObj);
        }
    }
}

4 个答案:

答案 0 :(得分:2)

是的,锁定是“重入”的,因此通话可以“双锁”(您的短语)lockObj。但请注意,它需要完全释放次数;你需要确保有一个相应的“ReleaseLock”来匹配“ObtainLock”。

但是,我确实建议让调用者lock(...)对你公开的某些属性更容易:

 public object SyncLock { get { return lockObj; } }

现在调用者可以(而不是obtainLock()):

lock(something.SyncLock) {
     //...
}

更容易做对。因为这是内部使用的相同的基础lockObj,所以即使obtainReleaseLock(等)使用内部代码锁定{{1} }。


随着上下文更清晰(评论),似乎SyncLockWait可能是这样做的:

Pulse

请注意,使用void SomeMethodThatMightNeedToWait() { lock(lockObj) { if(needSomethingSpecialToHappen) { Monitor.Wait(lockObj); // ^^^ this ***releases*** the lock (however many times needed), and // enters the pending-queue; when *another* thread "pulses", it // enters the ready-queue; when the lock is *available*, it // reacquires the lock (back to as many times as it held it // previously) and resumes work } // do some work, happy that something special happened, and // we have the lock } } void SomeMethodThatMightSignalSomethingSpecial() { lock(lockObj) { // do stuff Monitor.PulseAll(lockObj); // ^^^ this moves **all** items from the pending-queue to the ready-queue // note there is also Pulse(...) which moves a *single* item } } 时,您可能希望使用接受超时的重载,以避免永远等待;请注意,必须循环和重新验证是很常见的,例如:

Wait

答案 1 :(得分:1)

只有一个拥有者可以在给定时间持有锁;它是独家的。虽然锁定可以链接,但更重要的组件是确保获取和释放适当的次数,避免难以诊断线程问题。

当您通过lock { ... }打包代码时,您实际上是在输入和离开范围时调用Monitor.EnterMonitor.Exit

当您明确致电Monitor.Enter时,您将获得锁定,此时您需要致电Monitor.Exit以解除锁定。

答案 2 :(得分:1)

您必须使用Monitor才能使用此功能。请注意,如果你不小心你的锁并将它们放在单独的代码区域并且在相应的代码区域中释放可能会有风险,那么你就会遇到死锁和竞争条件

 Monitor.Exit(lockObj);

答案 3 :(得分:1)

这不起作用。

代码

lock(lockObj)
{
    // do stuff
}

被翻译成类似

的内容
Monitor.Enter(lockObj)
try
{
    // do stuff
}
finally
{
    Monitor.Exit(lockObj)
}

这意味着您的代码两次进入锁定但仅释放一次。根据{{​​3}},只有当Exit被调用Enter时,才会真正释放锁定,而代码中的情况并非如此。
摘要:您的代码在obtainReleaseLock调用时不会死锁,但线程永远不会释放lockObj上的锁定。您需要明确调用Monitor.Exit(lockObj),因此对Monitor.Enter的调用与Monitor.Exit的调用次数相匹配。