Home DEVELOPER C++20: Cooperative interruption of a thread with callback

C++20: Cooperative interruption of a thread with callback

0


As a reminder, in my previous article “Software Development: Cooperatively Suspending a Thread in C++20” I introduced the following program.

Advertisement





Rainer Grimm has been working as a software architect, team and training manager for many years. He enjoys writing articles on the programming languages ​​C++, Python and Haskell, but also frequently speaking at specialist conferences. On his blog Modern C++ he discusses his passion C++ in depth.

// invokeCallback.cpp

#include 
#include 
#include 
#include 

using namespace::std::literals;

auto func = ()(std::stop_token stoken) {               // (1)
  int counter{0};
  auto thread_id = std::this_thread::get_id();
  std::stop_callback callBack(stoken, 
                              (&counter, thread_id) {  // (2)
     std::cout << "Thread id: " << thread_id 
         << "; counter: " << counter << '\n';
  });
  while (counter < 10) {
    std::this_thread::sleep_for(0.2s);
    ++counter;
  }
};

int main() {
    
  std::cout << '\n';
    
  std::vector<:jthread> vecThreads(10);
  for(auto& thr: vecThreads) thr = std::jthread(func);
    
  std::this_thread::sleep_for(1s);                     // (3)
    
  for(auto& thr: vecThreads) thr.request_stop();       // (4)

  std::cout << '\n';
    
}

Each of the ten threads calls a lambda function func (1) on the callback (2) displays the thread ID and counter. Since the main thread (3) sleeps for a second and the child thread sleeps, the counter is 4 when the callback is called. call thr.request_stop() Triggers a callback on each thread.



One question was not answered in my last article:

std::stop_callbackThe constructor registers a callback function for this std::stop_tokenwhich is represented by the affiliate std::stop_source This callback function is called in either a thread request_stop() call, or in the thread that calls it std::stop_callback Built. If requested to pause before registration std::stop_callback happens, the callback is called in the thread that contains the std::stop_callback constructed. Otherwise the callback will be called in the thread request_stop call. call has been made request_stop() after the execution of the thread that contains the std::stop_callback The created, registered callback is never called.

You can have multiple callbacks for one or more threads by using this std::stop_token be entered. The C++ standard makes no guarantees about the order in which they will be executed.

// invokeCallbacks.cpp

#include 
#include 
#include 

using namespace std::literals;

void func(std::stop_token stopToken) {
    std::this_thread::sleep_for(100ms);
    for (int i = 0; i <= 9; ++i) {
       std::stop_callback cb(stopToken, (i) { std::cout << i; });
    }
    std::cout << '\n';
}

int main() {
    
    std::cout << '\n';
    
    std::jthread thr1 = std::jthread(func);
    std::jthread thr2 = std::jthread(func);
    thr1.request_stop();
    thr2.request_stop();

    std::cout << '\n';
    
}



Couple std::stop_source And std::stop_token can be seen as a general mechanism for sending signals. By doing so std::stop_token By copying, you can send signals to any unit that executes something. In the following example I use std::async, std::promise, std::thread And std::jthread in various combinations.

// signalStopRequests.cpp

#include 
#include 
#include 

using namespace std::literals;

void function1(std::stop_token stopToken, const std::string& str){
  std::this_thread::sleep_for(1s);
  if (stopToken.stop_requested()) std::cout << str 
                                            << ": Stop requested\n";
}

void function2(std::promise prom, 
               std::stop_token stopToken, const std::string& str) {
  std::this_thread::sleep_for(1s);
  std::stop_callback callBack(stopToken, (&str) { 
    std::cout << str << ": Stop requested\n"; 
  });
  prom.set_value();
}

int main() {

  std::cout << '\n';

  std::stop_source stopSource;                          // (1)

  std::stop_token stopToken = 
    std::stop_token(stopSource.get_token());            // (2)

  std::thread thr1 = 
    std::thread(function1, stopToken, "std::thread");   // (3)
    
  std::jthread jthr = 
    std::jthread(function1, stopToken, "std::jthread"); // (4)
    
  auto fut1 = std::async((stopToken) {                  // (5)
    std::this_thread::sleep_for(1s);
    if (stopToken.stop_requested()) std::cout 
      << "std::async: Stop requested\n";
  });

  std::promise prom;                              // (6)
  auto fut2 = prom.get_future();
  std::thread thr2(function2, std::move(prom), 
                   stopToken, "std::promise");

  stopSource.request_stop();                            // (7)
  if (stopToken.stop_requested()) 
    std::cout << "main: Stop requested\n";              // (8)

  thr1.join();
  thr2.join();

  std::cout << '\n';

}

thanks to stopSource (1) I can do that stopToken (2) The use for each running unit, e.g. B. std::thread (3), std::jthread (4), std::async (5) or std::promise (6). A std::stop_token It’s cheap to copy. (7) Solves stopSource.request_stop out. Also, the main thread receives a signal (8). I use in this example std::jthread. std::jthread And std::condition_variable_any There are explicit member functions to deal with cooperative interrupts more easily. More information can be found in the article “A Better Thread with C++20”.

I’ll take a break from writing for the next two weeks. After that I’ll get back to C++23 and follow C++26 for the first time.


(RME)

This is how the Sound One headphones from Anker brand, Soundcore, look

NO COMMENTS

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Exit mobile version