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

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

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

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

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

 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); } } } 

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

但是,我建议让调用者lock(...)你暴露的某些属性更容易:

  public object SyncLock { get { return lockObj; } } 

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

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

更容易做对。 因为这是在内部使用的相同的底层lockObj ,所以即使在锁定SyncLock代码中使用了obtainReleaseLock (etc),它也会与任一用法同步。


随着上下文更清晰(注释),似乎WaitPulse可能是这样做的:

 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您可能希望使用接受超时的重载,以避免永远等待; 请注意,必须循环和重新validation是很常见的,例如:

 lock(lockObj) { while(needSomethingSpecialToHappen) { Monitor.Wait(lockObj); // at this point, we know we were pulsed, but maybe another waiting // thread beat us to it! re-check the condition, and continue; this might // also be a good place to check for some "abort" condition (and // remember to do a PulseAll() when aborting) } // do some work, happy that something special happened, and we have the lock } 

您必须使用Monitor来实现此function。 请注意,如果您不小心锁定并将它们取出并在单独的代码区域中释放可能会造成危险,那么您可能会遇到死锁和竞争条件

  Monitor.Exit(lockObj); 

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

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

当您显式调用Monitor.Enter您将获得锁定,此时您需要调用Monitor.Exit来释放锁定。

这不起作用。

代码

 lock(lockObj) { // do stuff } 

被翻译成类似的东西

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

这意味着您的代码两次进入锁定但仅释放一次。 根据文档 ,如果在调用Exit时经常调用Lock,那么锁只会被线程释放,而代码中的情况并非如此。
简介:您的代码在调用obtainReleaseLock时不会死锁,但是线程永远不会释放lockObj上的锁定。 您需要显式调用Monitor.Exit(lockObj) ,因此对Monitor.Enter的调用与Monitor.Exit的调用次数相匹配。