For example, by initializing a semaphore to 0, threads can wait for an event to occur, and impose an ordering constraint, similar to a monitor using condition variables, but with memory: semaphore sem=0; // 0==locked for a semaphore
thread A
down(sem) // wait for thread B; note sem has memory!
// do stuff
thread B
// do stuff, then wake up A
up(sem)