The advantages of Pulse and Wait are that they can be used as building blocks for all other synchronization mechanisms, including mutexes, events, barriers, etc. There are things you can do with Pulse and Wait that cannot be done with any other synchronization mechanism in BCL.
All interesting things happen inside the Wait method. Wait will exit the critical section and put the thread in the WaitSleepJoin state, placing it in the wait queue. After a Pulse call, the next thread in the wait queue goes into the ready queue. After the thread switches to Running , it will again enable the critical section. This is important to repeat in a different way. Wait will release the lock and reload it atomically . No other synchronization mechanism has this feature.
The best way to imagine this is to try to replicate the behavior with a different strategy, and then see what might go wrong. Let's try this excerise with ManualResetEvent , because the Set and WaitOne seem similar. Our first attempt may look like this.
void FirstThread() { lock (mre) {
It should be easy to see that the code will be blocked. So what happens if we try this naive fix?
void FirstThread() { lock (mre) { // Do stuff. mre.Set(); // Do stuff. } } void SecondThread() { lock (mre) { // Do stuff. } while (!CheckSomeCondition()) { mre.WaitOne(); } lock (mre) { // Do stuff. } }
You see what could go wrong here? Since we did not atomize the re-enable of the lock after checking the wait state, another thread may enter the state and result in invalidation. In other words, another thread might do something that causes CheckSomeCondition to return false again until the next lock is re-locked. This can lead to many strange problems if your second block of code requires the condition to be true .
source share