为啥 Monitor.Wait() 和 Monitor.Pulse() 需要锁?
Posted
技术标签:
【中文标题】为啥 Monitor.Wait() 和 Monitor.Pulse() 需要锁?【英文标题】:Why do Monitor.Wait() and Monitor.Pulse() require a lock?为什么 Monitor.Wait() 和 Monitor.Pulse() 需要锁? 【发布时间】:2021-12-23 13:24:13 【问题描述】:要调用Monitor.Wait(obj)
或Monitor.Pulse(obj)
,首先必须通过lock(obj)
或Monitor.Enter(obj)
进入监视器。为什么 API 需要这个?
Thread synchronization: Wait and Pulse demystified 暗示了一些关于条件变量等的东西,但我不明白为什么我希望始终使用它。
我的意思是,如果只是实现依赖于获取的锁来完成Wait
或Pulse
的工作,为什么不只是在内部实现它作为它们的一部分呢?为什么不将Wait
和Pulse
与Monitor
中的其他行为完全解耦,让开发人员随心所欲地使用这些功能?这对开发人员来说似乎是一个毫无意义的负担,但有充分的理由让它成为这样吗?
更具体地说,Monitor.Wait(obj)
throws SynchronizationLockException
如果“Wait
不是从同步代码块中调用”。 Monitor.Pulse(obj)
throws SynchronizationLockException
如果“调用线程不拥有指定对象的锁”。
【问题讨论】:
您是在问“为什么lock
是使用Monitor
类实现的,而不是相反”或“为什么要使用Wait
/Pulse
释放锁需要获取先用lock
或Enter
锁定?”或者你问更多关于为什么像***.com/questions/1559293/…这样的存在?
抱歉,我已经编辑了原始问题以使其更清晰。
不,您的编辑并没有使问题更清楚-您的意思已经很清楚了(例如,每个人都可以查看文档以查看使用模式),但仍不清楚您为什么这么认为对于“作为单个操作获取和释放锁”将具有一定的价值。如果你能解释一下你希望Wait
做什么,那就太好了,这样人们就可以尝试澄清这一点。 (旁注你显然知道"EDIT:" should not be added 要发布 - 除非你有一些特别的声明,否则避免这样做)
Related
@Jake1234 有点不清楚。随意编辑整个内容,或者关闭它并写一个新问题
【参考方案1】:
不持有锁,你肯定会有竞争条件。假设线程 A 将调用 Wait
,而线程 B 将调用 Pulse
。在线程 B 发送脉冲之前,线程 A已经在等待,这一点很重要。如果线程 B 在线程 A 开始等待之前 发送脉冲,那么脉冲什么也不做,线程 A 将无限期地等待。我们如何知道线程 A 正在等待?好吧,它可以在它开始等待之前发送一条消息(例如,设置一个标志或将一个项目放入队列中),但是如果发送消息然后另一个线程在它开始等待之前被调度呢?仅仅因为它已经发送了它打算开始等待的消息,并不意味着它实际上已经开始等待了!知道线程 A 确实进入等待的唯一方法是让 A 在发送消息时持有锁,在它开始等待时原子地释放锁,让 B 获取锁,然后观察 A 的消息。
现在,好吧,也许你可以设计一种情况,B 确定 A 已经开始等待,释放锁,做一些其他事情,然后想在释放锁后发送一个脉冲来唤醒 A,但是你会当您想再次使用监视器时遇到类似的问题,或者如果您想有更多的线程等待或脉冲。或者也许你说你不在乎错过脉冲,你只会继续发送更多......但随后你就会转向投票并浪费Pulse
和Wait
的好处。
简而言之,您需要在等待之前持有锁,因为这是可靠地传达有实体在等待脉冲的唯一方法。您需要在发出脉冲之前保持锁定,因为这是可靠地发现有实体准备好接收脉冲的唯一方法。如果没有这些行为,Monitor 将不会成为在其之上构建更高级同步机制的有用原语。
【讨论】:
【参考方案2】:因为 monitor.wait 的目的是释放对象上的锁。这些是静态方法,因此除了通过参数外,没有状态指示应该释放哪个对象,因此必须传入它
另外,这里的目的是允许锁用于某种目的,然后在 Monitor.Wait 中释放。将其锁定然后由监视器解锁不会有任何效果
【讨论】:
抱歉给您带来了困惑,这不是我要问的。以上是关于为啥 Monitor.Wait() 和 Monitor.Pulse() 需要锁?的主要内容,如果未能解决你的问题,请参考以下文章
C#简单理解 Monitor.Wait 与 Monitor.Pulse