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?

David
  • 27,652
  • 18
  • 89
  • 138
  • 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. – nos Jul 11 '12 at 23:21
  • @nos I know they will run sequentially in this example - that's not the problem. I can call `lock.unlock()` after `cv.wait` – David Jul 11 '12 at 23:26
  • then what is ? Nothing continues once you resume from the breakpoint ? – nos Jul 11 '12 at 23:28
  • @nos Yes, the 2nd thread is just left waiting, when I continue from the breakpoint. Without a breakpoint it works fine. – David Jul 11 '12 at 23:33
  • 1
    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. http://www.stanford.edu/class/cs140/cgi-bin/lecture.php?topic=locks has some good explanations. – VSOverFlow Jul 12 '12 at 18:28

1 Answers1

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();
Nemo
  • 70,042
  • 10
  • 116
  • 153
  • 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 `cout`s, then a 1s pause, and then the other. However I see them both after the 1s pause. Any insight on that? – David Jul 11 '12 at 23:37
  • 1
    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 output – Nemo Jul 11 '12 at 23:40
  • 2
    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. – Nemo Jul 11 '12 at 23:42
  • Thanks again. It seems like only 1 thread can be `wait`ing 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? – David Jul 11 '12 at 23:46
  • 3
    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`](http://en.cppreference.com/w/cpp/thread/condition_variable/wait) are to release the mutex, then to obtain it again when the condition is signaled. – Nemo Jul 11 '12 at 23:52
  • "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.) – Bartosz Milewski Jul 12 '12 at 18:08