简体   繁体   中英

Why do Monitor.Wait() and Monitor.Pulse() require a lock?

Why does calling these require a lock?

https://www.codeproject.com/Articles/28785/Thread-synchronization-Wait-and-Pulse-demystified

This post hints at something about conditional variables etc., but I don't see why I'd want that to always be used.

I mean if it's necessary to acquire the lock before calling Wait() or Pulse(), why not just implement that internally as a part of them? Or why not make Wait and Pulse atomic and let the developer use these functions as they want?

here is what I mean by "requires a lock":

Monitor.Pulse(_lock) will not work, but lock (_lock) { Monitor.Pulse(_lock); } lock (_lock) { Monitor.Pulse(_lock); } does.

because the purpose of monitor.wait is to release the lock on an object. These are static methods so there is no state indicating what object should be released except via a parameter, hence it must be passed in

Also the aim here is to allow the lock to be used for some purpose, and then released in Monitor.Wait. Having it lock and then unlocked by monitor wouldn't achieve anything

Without holding the lock, you will definitely have a race condition. Suppose thread A is going to call Wait and thread B is going to call Pulse . It's important that thread A is already waiting before thread B sends the pulse. If thread B sends the pulse just before thread A starts waiting, then the pulse does nothing and thread A will wait indefinitely. How do we know that thread A is waiting? Well, it could send a message (eg setting a flag or putting an item in a queue) before it starts waiting, but then what if sends the message and then another thread gets scheduled before it begins waiting? Just because it has sent the message that it intends to start waiting, doesn't mean that it has yet actually begun waiting! The only way to know that thread A certainly has entered the wait is for A to hold the lock while sending its message, atomically relase the lock as it begins waiting, for B to acquire the lock and then observe A's message.

Now, okay, maybe you could contrive a situation where B determines that A has begun waiting, releases the lock, does some other things and then wants to send a pulse to wake up A after releasing the lock, but you'll run into similar problems when you want to re-use the monitor a second time, or if you want to have more threads waiting or pulsing. Or maybe you say you don't care about missing the pulse, you'll just keep sending more... but then you're devolving into polling and squandering the benefits of Pulse and Wait .

In short, you need to hold the lock before waiting because it's the only way to reliably communicate that there is an entity waiting for a pulse. You need to hold the lock before pulsing because it's the only way to reliably discover that there is an entity ready to receive a pulse. Without these behaviours the Monitor would not be a useful primitive for building more advanced synchronisation mechanisms on top of.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM