On Wed, 21 Oct 2009 14:50:32 -0400, dsimcha <dsim...@yahoo.com> wrote:

== Quote from Bartosz Milewski (bartosz-nos...@relisoft.com)'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

Reply via email to