4

I am using std::conditional_variable for timing a signal in a multi-threaded program for controlling the flow of various critical sections. The program works but during exit I am compelled to use a predicate (kill_ == true) to avoid destroying of threads which are still waiting on the std::conditional_variable ::wait(). I don't know if its the proper way to destroy all the waiting threads, advice solicited. Here's a code snippet:

class timer
{
  // ...
       timer(std::shared_ptr<parent_object> parent,const bool& kill)
         :parent_(parent),kill_(kill){}

  private:
       std::condition_variable cv_command_flow_;
       std::mutex mu_flow_;
       const bool& kill_;
       std::shared_ptr<parent_object> parent_;
};

void timer::section()
{
       auto delay = get_next_delay();

       std::unique_lock<std::mutex> lock(mu_flow_);
       std::cv_command_flow_.wait_until(lock,delay,[] { return kill_ == true; });

       if( kill_) return;

       parent_->trigger();

       std::cv_command_exec_.notify_all();
}
ark1974
  • 615
  • 5
  • 16
  • Have you tried [`notify_all`](http://www.cplusplus.com/reference/condition_variable/condition_variable/notify_all/)? – iammilind Jan 13 '17 at 05:33
  • 1
    terminology issue: do you want terminate threads or let them run to the end. what do you consider by 'thread destruction' ? – Oleg Bogdanov Jan 13 '17 at 05:48

1 Answers1

3

This is generally how I handle the destruction of my waiting threads. You'll want a code section such as this where you want to perform clean up (in a class destructor, the main thread before process exit, etc.):

{
  std::lock_guard<std::mutex> lock(mu_flow);
  kill_ = true;
}
cv_command_exec_.notify_all();
thread1.join();

I'm assuming that timer::section() was executing within some thread std::thread thread1.

Ownership duration of the mutex is controlled by the scoped block. You'll want the mutex held only when you set kill_ = true and released before you call .notify_all() (otherwise the woken thread might find the lock still held and go back to sleep).

Of course, std::unique_lock usage would look like:

std::unique_lock<std::mutex> lock(mu_flow);
kill_ = true;
lock.unlock();
cv_command_exec_.notify_all();
thread1.join();

It's personal preference to a large degree ... both code sections accomplish the same task.

Matthew M.
  • 392
  • 2
  • 11
  • Thanks for the feedback. Timer is a subset of another object and it will exist throughout the life of the program ( thread.detach() used). And the signal kill_ ( = const) will be set from the main program only during exit. std::unique_guard is preferred over lock_guard to allow lock/unlock. – ark1974 Jan 13 '17 at 10:57
  • Added comments on std::lock_guard and std::unique_lock. – Matthew M. Jan 13 '17 at 14:46