Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::condition_variable without a lock

I'm trying to synchonise a set of threads. These threads sleep most of the time, waking up to do their scheduled job. I'm using std::thread for them.

Unfortunately, when I terminate the application threads prevent it from exiting. In C# I can make a thread to be background so that it will be termianted on app exit. It seems to me that equavalint feature is not availabe at C++.

So I decided to use a kind of event indicator, and make the threads to wake up when the app exits. Standard C++11 std::condition_variable requires a unique lock, so I cannot use it, as I need both threads to wake up at the same time (they do not share any resources).

Eventually, I decided to use WinApi's CreateEvent + SetEvent+WaitForSingleObject in order to solve the issue.

I there a way to achieve the same behavior using just c++11?

Again, what do I want:

  1. a set of threads are working independently and usually are asleep for a particular period (could be different for different threads;
  2. all threds check a variable that is availabe for all of them whether it is a time to stop working (I call this variable IsAliva). Actually all threads are spinning in loop like this:

    while (IsAlive) {
      // Do work
      std::this_thread::sleep_for(...);
    }
    
  3. threads must be able to work simultaneously, not blocking each other;
  4. when the app is closed and event is risen and it makes the thread to wake up right now, no matter the timeout;
  5. waken up, it checks the IsAlive and exits.
like image 255
Ivan Avatar asked Jul 12 '26 10:07

Ivan


1 Answers

yes you can do this using standard c++ mechanisms of condition variables, a mutex and a flag of somekind

// Your class or global variables
std::mutex deathLock;
std::condition_variable deathCv;
bool deathTriggered = false;

// Kill Thread runs this code to kill all other threads:
{
    std::lock_guard<std::mutex> lock(deathLock);
    deathTriggered = true;
}
deathCv.notify_all();

// You Worker Threads run this code:
while(true)
{
   ... do work

   // Now wait for 1000 milliseconds or until death is triggered:
   std::unique_lock<std::mutex> lock(deathLock);
   deathCv.wait_for(lock, std::chrono::milliseconds(1000), [](){return deathTriggered;});

   // Check for death
   if(deathTriggered)
   {
      break;
   }
}

Note that this runs correctly in the face of death being triggered before entering the condition. You could also use the return value from wait_for but this way is easier to read imo. Also, whilst its not clear, multiple threads sleeping is fine as the wait_for code internally unlocks the unique_lock whilst sleeping and reacquires it to check the condition and also when it returns.

Finally, all the threads do wake up 'at the same time' as whilst they're serialised in checking the bool flag, this is only for a few instructions then they unlock the lock as they break out of the loop. It would be unnoticeable.

like image 134
Mike Vine Avatar answered Jul 15 '26 01:07

Mike Vine



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!