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:
Where is the callback executed?
std::stop_callback
The constructor registers a callback function for this std::stop_token
which 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.
More than just callbacks
// 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';
}
A common mechanism for sending signals
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”.
What will happen next?
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)
