Okay, so we've got our semaphore class done. Turns out, that'll be the easiest of the thread-synchronization classes, as it's the only one that is supported directly on both platforms. Thus, we're gonna have to start doing some emulating. Depending on your personality, this may be where things get interesting.
The next class we're going to write is an encapsulation of an event. An event is basically a boolean that threads can wait on - it is either set or cleared (if you're familiar with POSIX programming, an event is very similar to a condition variable, but not exactly the same). When the event gets set, threads waiting on the event get released.
Now, there are two types of events: manual-reset and auto-reset. A manual-reset event functions exactly like a boolean: you set it, and it remains set until you clear it, and vice versa. All threads that are waiting when the event gets set will be released, and any threads attempting to wait on a set event will not wait.
An auto-reset event is actually more useful. An auto-reset event will remain cleared until it gets set, but once set, will automatically get cleared after exactly one thread is released from waiting. If a thread is waiting when the event gets set, that thread will immediately get released, and the event will remain cleared; but if no threads are waiting when the event gets set, the event will remain set until a thread tries to wait on it, at which time the thread will not wait, and the event will get cleared. Really, you can imagine an event as being a semaphore whose value never goes above 1, no matter how many times it is posted; alternatively, you could consider it a thread queue.