Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pause and resume thread from parent thread in cpp

I am using cpp thread library. I have a parent thread T1 which has a child thread T2. T2 will only loop through some items and do some processing. I need to pause and resume T2 from T1 using functions calls from T1. T1 and T2 belongs to same class. I need this to stop processing data when a particular event comes to T1; Please don't suggest any other thread implementation library.

    C::t2_func{
      for(int i=0;i<data.size();i++)
        process_data(data[i]);
    }
    C::spawn(){
       t2 = std::make_unique<std::thread>(std::bind(&C::t2_func, this));
    }
    C::pause(){
         //pause t2
    }
    C::resume(){
         //resume t2
    }
like image 784
Nithin Mohan Avatar asked Jan 06 '23 08:01

Nithin Mohan


1 Answers

While it's not possible to pause an STL thread entirely externally to the thread (some platform-specific threads with more extensive thread management functions might support it), a thread can be paused with the cooperation of the thread. Note that this means pausing won't happen at an arbitrary point during thread execution, but only at specific points where the thread function supports pausing.

The core of pausing a thread is quite simple: a flag that indicates whether to run or pause. Accessing shared thread data, such as the flag, must be synchronized; this is a secondary aspect that necessarily makes the implementation more involved, though not exceedingly so.

Synchronization is achieved through locks (such as lock_guard and unique_lock) on a mutex; the mutex is what ensures critical sections of code are mutually exclusive (i.e. won't be interrupted by one another), and the locks are responsible for managing locking and unlocking the mutex.

Additionally, a condition variable is used to signal that the thread should run: the pausing thread waits until the flag means "run", and the resume function notifies that the flag is "run". Since the condition variable accesses shared data, it also uses a lock on the mutex. std::condition_variable supports two ways of waiting: one that simply waits for a notification but that can suffer from spurious wake-ups, and another that takes a callable predicate and will wait both for notifications and for the predicate to return true (which allows spurious wake-ups to be ignored). Generally speaking, the latter is what you'll want.

With the assumption that the thread functions have void return values, a sample implementation could be:

// shared data, threading primitives
bool pause = false;
std::condition_variable cv;
std::mutex m;

// T2's main thread function
void C::t2_func() {
  for(int i = 0; i < data.size(); i++){
    // wait until unpause is signaled
    nap();
    
    // do some work
    process_data(data[i]);
  }
}

void C::spawn() {
   t2 = std::make_unique<std::thread>(std::bind(&C::t2_func, this));
}

// thread management
void C::nap() {
    // must use a `unique_lock` with a `condition_variable`, not a `lock_guard`
    std::unique_lock<decltype(m)> lk(m);
    cv.wait(lk, []{ return ! pause; });
}

void C::pause() {
     //pause
     std::lock_guard<decltype(m)> lk(m);
     pause = true;
}

void C::resume() {
     std::lock_guard<decltype(m)> lk(m);
     pause = false;
     cv.notify_one();
     //resume t2
}
like image 98
v78 Avatar answered Jan 14 '23 01:01

v78