Your remark about adding Thread.Sleep(1)
is really the answer. The Pulse
method doesn't guarantee anything for the waiting object - it is just released to the ready queue from which it proceeds as an ordinary thread. There is no trace anywhere about the call to the Pulse
. Thus, after you call Pulse
the application behaves as ordinary two-threaded application, and there are two threads in the ready queue - the ToUpper
thread and the Run
thread. So without Thread.Sleep(1)
it can happen (without it it's also possible I guess, just less probable) that the Run
thread obtains the lock first.
Another important remark from the second link I've provided:
An important feature of Monitor.Pulse is that it executes asynchronously, meaning that it doesn't itself block or pause in any way.
For this scenario the AutoResetEvent
class seems more suitable. Also, in the second link you can find an example of a producer-consumer scenario with Wait
and Pulse
.