== Quote from Steven Schveighoffer ([email protected])'s article > On Wed, 21 Oct 2009 14:50:32 -0400, dsimcha <[email protected]> wrote: > > == Quote from Bartosz Milewski ([email protected])'s article > >> dsimcha Wrote: > >> > void main() { > >> > condition = new Condition( new Mutex() ); > >> > auto T = new Thread(&waitThenPrint); > >> > T.start(); > >> > condition.notify(); // Never wakes up and prints FOO. > >> > } > >> Your program terminates immediately after sending the notification. You > >> need to > > stall the exit until the other thread has a chance to wake up. > > > > Thanks. I've implemented this, along w/ one other suggestion from > > another poster. > > Here's the new program. It still doesn't work. > Here is a major flaw in your logic: > A condition is a *signal* not a *flag*. In order to catch the signal you > have to be listening for it. It's not like a semaphore. > What you need in order to use a condition is a flag that is protected by > the lock. > Generally, the model is: > thread1() > { > lock(mutex) > { > set(flag); > condition.notify(); > } > } > thread2() > { > lock(mutex) > { > while(!flag) > condition.wait(); > unset(flag); // we received the signal, clear it. > } > } > The condition is basically a way to give control of waking up a thread to > another thread. But the condition is not the, um... condition you are > waiting for :) You still need a state variable to say "hey, you should > continue now, the state is correctly set". > Think of the flag like a mailbox flag. You only put it up *after* you put > mail in the mailbox, and the mailman lowers the flag when he gets the mail > out. > There are lots of threading tutorials you can probably read to get it. > But basically, I can break down what exactly happens, I'll do 2 scenarios: > Scenario 1: > 1. thread2 locks the mutex, which protects the flag. It sees that the > flag is unset, so it waits on the condition. This *atomically* unlocks > the mutex and enters the thread into the list of threads to wake up when > the condition is signaled. > 2. thread1 now can lock the mutex, and sets the flag. It notifies the > condition, which wakes up thread2. > 3. thread2 *remains asleep* until it can reacquire the lock. thread1 > unlocks the mutex after leaving the scope. > 4. thread2 wakes up after reacquiring the mutex, and repeats the > while-loop, seeing that the flag is now set. > 5. thread2 unsets the flag and unlocks the mutex, continuing. > Scenario 2: > 1. thread1 locks the mutex. > 2. thread2 sleeps because it cannot lock the mutex. > 3. thread1 sets the flag, then signals the condition. Since nobody is > listening *this doesn't affect anything*. > 4. thread1 exits the scope, releasing the mutex. > 5. thread2 now acquires the lock, sees the flag is set, and doesn't even > wait on the condition, unsets the flag, exits the scope, and unlocks the > mutex. > You can see how the locking is important to protect the atomicity of > setting the flag, and it is *really* important that the condition wait > atomically unlocks the mutex and enters the thread into the condition's > wakeup queue. So generally speaking: rule 1, don't do anything with a > condition unless the mutex it uses is locked. rule 2, always have a state > that is protected by the same lock that the condition is waiting with. > You also may wonder why there is even a while loop, I mean, why recheck > the flag after the condition is signalled? Well, in this case, it's not > required, but it's very good practice. When you have a case where the > flag is not a flag, but a multi-state variable, and you are waiting for a > specific state, one thread might signal every time the state changes. > Well, you don't want to continue until you get the state that you want, so > you need to re-check the state. Another case is if you have several > instances of thread2, and you only want to release one of them with the > signal. > Hope this helps. > -Steve
Thank you. This was *extremely* helpful. I'll read over it in more detail when I get back to hacking my futures/parallel foreach lib.
