Lets say I have to orchestrate a synchronization algorithm in .Net 3.5 SP1 and any of the synchronization primitives listed in the title fit perfectly for the task.
From a performance perspective, is any single one of those more performant than the others?
I ask this because I have been coding for a while now, but without proper knowledge on the subject.
If you can, go with Monitor. It's similar to a CRITICAL_SECTION. AutoResetEvent/ManualResetEvent might have slightly more overhead, since these can be shared by different processes, whereas a Monitor belongs to a single process.
WaitHandles look very similar to Wait/Pulse Constructs, but the difference is in detail: The WaitHandles Set method, sets the Signal even if no thread is waiting. This means if you call Set in a thread and then call WaitOne in another thread on the same waithandle afterwards, the second thread will continue. Wait and Pulse are different, Pulse only signals a thread that is already in the waiting queue. This means if you call Pulse in a thread and then call Wait in another thread on the same object afterwards, the second thread will wait forever (deadlock). You've got to be extremely carefull if using Wait and Pulse, only use it if you know what you are doing otherwise you might just be lucky...
To create the behaviour of a WaitHandle yourself using Monitor, weather AutoReset or ManualReset, you've got to do way more than a simple Wait/Pulse Construct. Just use the Tools you need to get the Job done:
If you can't synchronize threads with simple locking or atomic operations, think of using WaitHandles. If you can't synchronize threads with WaitHandles, think of using Wait and Pulse.
Wait and pulse are fine, if you follow some simple rules:
- You may not bother to lock if if locking isn't needed for your object and you can tell in advance that it's ready; if, however, it looks like you'll have to wait, you must acquire the lock and then make certain you still really do have to wait before you actually do it; you should also in most cases assume that you may randomly be woken up whether or not conditions are ready, so you should recheck the conditions every time you wake up and re-wait if needed.
- A pulse must be given after conditions have been set up that will allow waiting code to continue.
- If you have something like a "quit" flag, it may be helpful to have every piece of code that acquires the lock test the quit flag after releasing the lock and, if the flag is set, reacquire the lock and send a pulse. You could then have a quit routine set the flag, try acquiring the lock with zero timeout, and send a pulse only if the lock could be acquired. If the acquisition failed, you could be assured some other piece of code will send the necessary pulse. This will avoid any possibility of the 'quit' routine getting stuck at the lock.