They say it's the only safe way... use a barrier method every time.
Seriously, though. I was pleased to see that .Net 2.0 contained an implementation of Semaphore. It contains a lot of cool stuff. Something it doesn't have, though, is a barrier. Barriers are a synchronization tool that can be used to ensure no thread starts working until the desired number of threads are ready.
A good real life analogy for a barrier is the starter in a race. The starter makes sure that all of the runners (threads) are at their marks. When all of the contestants are ready, the starter fires his fake pistol and they're off. That's what the barrier does. It's not the most commonly used synchronization tool - I use it mostly in tests - but it can come in handy.
Below, I've implemented a barrier as a wait handle. I commented it a little but, frankly, I'm tired. It should be pretty clear how it works.
Google blogging screws up one's code, so I've moved the example to a seperate text file.