Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use a boost condition variable to wait for a thread to complete processing?

Tags:

I am using a conditional variable to stop a thread until another thread has completed processing it's task queue (long story). So, on one thread I lock and wait:

boost::mutex::scoped_lock lock(m_mutex); m_condition.wait(lock); 

Once the other thread has completed it's tasks, it signals the waiting thread as follows:

boost::mutex::scoped_lock lock(m_parent.m_mutex); m_parent.m_condition.notify_one(); 

The problem I am seeing is that the waiting thread does not stop waiting unless I set a breakpoint on the instructions following it (I am using xcode, fyi). Yes, this seems strange. Does anyone know why this might be happening? Am I mis-using the condition variable?

like image 375
MM. Avatar asked Jun 03 '13 22:06

MM.


2 Answers

Yes, you are misusing the condition variable. "Condition variables" are really just the signaling mechanism. You also need to be testing a condition. In your case what might be happening is that the thread that is calling notify_one() actually completes before the thread that calls wait() even starts. (Or at least, the notify_one() call is happening before the wait() call.) This is called a "missed wakeup."

The solution is to actually have a variable which contains the condition you care about:

bool worker_is_done=false;  boost::mutex::scoped_lock lock(m_mutex); while (!worker_is_done) m_condition.wait(lock); 

and

boost::mutex::scoped_lock lock(m_mutex); worker_is_done = true; m_condition.notify_one(); 

If worker_is_done==true before the other thread even starts waiting then you'll just fall right through the while loop without ever calling wait().

This pattern is so common that I'd almost go so far as to say that if you don't have a while loop wrapping your condition_variable.wait() then you always have a bug. In fact, when C++11 adopted something similar to the boost::condtion_variable they added a new kind of wait() that takes a predicate lambda expression (essentially it does the while loop for you):

std::condition_variable cv; std::mutex m; bool worker_is_done=false;   std::unique_lock<std::mutex> lk(m); cv.wait(lk, []{return worker_is_done;}); 
like image 112
Wandering Logic Avatar answered Nov 06 '22 09:11

Wandering Logic


I had implemented an example that illustrates how to use boost condition, based in the discussion.

#include <iostream>  #include <boost/asio.hpp> #include <boost/thread/mutex.hpp> #include <boost/thread/thread.hpp>  boost::mutex io_mutex; bool worker_is_done = false; boost::condition_variable condition;  void workFunction() {     std::cout << "Waiting a little..." << std::endl;     boost::this_thread::sleep(boost::posix_time::seconds(1));     worker_is_done = true;     std::cout << "Notifying condition..." << std::endl;     condition.notify_one();     std::cout << "Waiting a little more..." << std::endl;     boost::this_thread::sleep(boost::posix_time::seconds(1)); }  int main() {     boost::mutex::scoped_lock lock(io_mutex);     boost::thread workThread(&workFunction);      while (!worker_is_done) condition.wait(lock);     std::cout << "Condition notified." << std::endl;     workThread.join();     std::cout << "Thread finished." << std::endl;      return 0; } 

Boost condition variable example

like image 40
Fernando Avatar answered Nov 06 '22 10:11

Fernando