Condition Mutexes
Steven Schveighoffer
schveiguy at yahoo.com
Wed Oct 21 13:49:51 PDT 2009
On Wed, 21 Oct 2009 14:50:32 -0400, dsimcha <dsimcha at yahoo.com> wrote:
> == Quote from Bartosz Milewski (bartosz-nospam at 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
More information about the Digitalmars-d
mailing list