Many people tend to get hung up on the more subtle aspects of the Win32 events as they create designs that make certain incorrect assumptions on how Win32 events work.
There are really two different types of Win32 events, Auto-Reset and Manual-Reset events.
These events will automatically transition themselves back to a non-signaled state as soon a one thread that is waiting on the event is released.
Using SetEvent with an Auto-Reset Event will result in a single thread being unblocked prior to the event returning to the non-signaled state. If no threads are currently waiting on the event the event the next thread to wait on the event will be unblocked (unless of course ResetEvent or PulseEvent are called prior to a thread waiting on the event.)
Using PulseEvent with an Auto-Reset Event will result in a single thread waiting on the event to become unblocked prior to the event returning to the non-signaled state. If no thread are currently waiting on the event, the event is still returned to the non-signaled state.
Manual Reset Events
These events will remain in their Signaled state until either PulseEvent or ResetEvent are called on their events.
If SetEvent is called on a Manual Reset Event, the event will remain signaled until ResetEvent or PulseEvent is called. This will result in all threads waiting on the Manual Reset Event (along with future threads while it is still signaled) to become unblocked.
If PulseEvent is called on a Manual Reset Event, the event is set to a signaled state, all threads currently waiting on the event are unblocked and the event then returns to a non-signaled state.
Win32 Events and Thread Priorities
Some people make the assumption that the highest priority thread that is waiting on a Win32 event will be the thread that gets released. This should never be assumed as there are no guarantees that the highest priority thread will be the thread that is released.
Win32 Events with Multiple Waiting Threads
The only way to ensure that all threads that are waiting on a Win32 Event are to use a Manual Reset event teamed with calls to PulseEvent or SetEvent. Using an Auto-Reset event when you have multiple waiting threads that should be released together will always result in not all threads being released.