Monitor.Pulse and PulseAll requires that the lock it operates on is locked at the time of call. This requirement seems unnecessary and detrimental for performance. My first idea was that this results in 2 wasted context switches, but this was corrected by nobugz below (thanks). I am still unsure whether it involves a potential for wasted context switches, as the other thread(s) which were waiting on the monitor are already available for the sheduler, but if they are scheduled, they will only be able to run a few instructions before hitting the mutex, and having to context-switch again. This would look much simpler and faster if the lock was unlocked before invoking the Monitor.Pulse.
Pthread condition variables implement the same concept, but it does not have the above described limitation: you can call pthread_cond_broadcast even if you do not own the mutex. I see this as a proof that the requirement is not justified.
Edit: I realize that a lock is required to protect the shared resource that is usually changed before the Monitor.Pulse. I was trying to say that that lock could have been unlocked after access to the resource but before the Pulse, given that Monitor would support this. This would help in limiting the lock to the shortest time during which the shared resource is accessed. As such:
void f(Item i)
{
lock(somequeue) {
somequeue.add(i);
}
Monitor.Pulse(somequeue); // error
}