Control multithreaded flow with condition_variable
Asked Answered
M

1

7

I haven't wrapped my head around the C++11 multithreading stuff yet, but I'm trying to have multiple threads wait until some event on the main thread and then all continue at once (processing what happened), and wait again when they're done processing... looping until they're shut down. Below isn't exactly that - it's a simpler reproduction of my problem:

std::mutex mutex;
std::condition_variable cv;

std::thread thread1([&](){ std::unique_lock<std::mutex> lock(mutex); cv.wait(lock);  std::cout << "GO1!\n"; });
std::thread thread2([&](){ std::unique_lock<std::mutex> lock(mutex); cv.wait(lock);  std::cout << "GO2!\n"; });

cv.notify_all(); // Something happened - the threads can now process it

thread1.join();
thread2.join();

This works... unless I stop on some breakpoints and slow things down. When I do that I see Go1! and then hang, waiting for thread2's cv.wait. What wrong?

Maybe I shouldn't be using a condition variable anyway... there isn't any condition around the wait, nor is there data that needs protecting with a mutex. What should I do instead?

Monika answered 11/7, 2012 at 23:11 Comment(5)
Only one thread at a time can run when the condition is signalled, since that thread holds the mutex. The other thread will be blocked. Is what what you want ? If not, release the mutex immediately after you're signalled.Overarm
@Overarm I know they will run sequentially in this example - that's not the problem. I can call lock.unlock() after cv.waitMonika
then what is ? Nothing continues once you resume from the breakpoint ?Overarm
@Overarm Yes, the 2nd thread is just left waiting, when I continue from the breakpoint. Without a breakpoint it works fine.Monika
notify will work only when both T1 & T2 are waiting for the condition variable. However they can wait for the condition variable only after acquiring the lock. In your implementation once T1 or T2 acquires the lock it does not release it for the other thread to acquire it and wait for the condition. stanford.edu/class/cs140/cgi-bin/lecture.php?topic=locks has some good explanations.Terminator
R
5

You are on the right track...

Just add a Boolean (protected by the mutex, indicated by the condition variable) that means "go":

std::mutex mutex;
std::condition_variable cv;
bool go = false;

std::thread thread1([&](){ std::unique_lock<std::mutex> lock(mutex); while (!go) cv.wait(lock);  std::cout << "GO1!\n"; });
std::thread thread2([&](){ std::unique_lock<std::mutex> lock(mutex); while (!go) cv.wait(lock);  std::cout << "GO2!\n"; });

{
    std::unique_lock<std::mutex> lock(mutex);
    go = true;
    cv.notify_all(); // Something happened - the threads can now process it
}

thread1.join();
thread2.join();
Rimmer answered 11/7, 2012 at 23:22 Comment(6)
Thanks, this works - but can you explain the results I saw without the bool? Also, as a test, instead of notify_all I changed to: cv.notify_one(); std::this_thread::sleep_for(std::chrono::seconds(1)); cv.notify_one(); - expecting to see 1 of the couts, then a 1s pause, and then the other. However I see them both after the 1s pause. Any insight on that?Monika
Re: notify_one -- The other thread was blocked waiting for the mutex when the main thread did its notify. Then the main thread dropped the mutex, so the other thread obtained it, continued, saw that "go" was true, and printed the output. If you want the threads to "go" one at a time, you need to set "go = false" again after printing the outputRimmer
As for explaining your original results, the "notify" only notifies threads that are already waiting on the condition variable. If you call notify while nobody is waiting, then when the other thread tries to wait it will block.Rimmer
Thanks again. It seems like only 1 thread can be waiting on a condition_variable at a time, the others will be waiting on the mutex. If multiple threads can't wait on a condition_variable at a time, what is notify_all supposed to do? If they can, how?Monika
Not true... Many threads can certainly be waiting on the same condition variable. What you are seeing is an artifact of how your system decided to schedule the threads (and how to hand over the mutex when several threads are waiting on it). The semantics of wait are to release the mutex, then to obtain it again when the condition is signaled.Rimmer
"It seems like only 1 thread can be waiting on a condition_variable at a time, the others will be waiting on the mutex." It's not true because wait immediately unlocks the lock. It acquires it back right before returning. This is the reason why wait must take a lock as an argument. (Notice that it must do the unlock and start the wait in one atomic operation, so there is no window for notifications to be lost.)Meredithmeredithe

© 2022 - 2024 — McMap. All rights reserved.