very new to posting here, so apologies if it's in the wrong place.
I'm a hobby coder, so my experience is limited and while I learned C++ some years ago, I have to re-learn it periodically. Now is such a time.
I'm trying to understand jthread
and it's associated stop_token
mechanism.
I've finally managed to get the threads working (using VS 2022 with language standard set to C++ 20). Now though I can't get them to stop. I can't work out what I'm doing wrong here as the main thread runs to the end, but the stop_request
calls seem to do nothing:
#include <iostream>
#include <chrono>
#include <thread>
#include <functional>
class Testing {
int updates{ 0 };
public:
void update(std::stop_token s, int i, char l) {
while (!s.stop_requested()) {
updates++;
std::cout << updates << " " << l << "\n";
std::this_thread::sleep_for(std::chrono::milliseconds(i));
}
std::cout << l << " stopped\n";
}
};
int main() {
Testing test_1, test_2;
std::stop_token s_1, s_2;
std::jthread t_1(&Testing::update, &test_1, s_1, 2300, 'A');
std::jthread t_2(&Testing::update, &test_2, s_2, 700, 'B');
std::this_thread::sleep_for(std::chrono::milliseconds(10000));
std::cout << "Main thread done\n";
t_1.request_stop();
if (t_1.joinable()) { t_1.join(); }
t_2.request_stop();
if (t_2.joinable()) { t_2.join(); }
return 0;
}
If anyone can help me here I'd be most grateful!
There are 2 issues in your code:
std::stop_token
should not be instantiated separately and passed to the std::jthread
. Rather it already exists internally in the std::jthread
.std::stop_token
associated with the std::jthread
is passed automatically as a first argument to the thread function. But it does not work when you use a class method, that already requires an implied this
argument.A Solution:
You can use a lambda (that captures the Testing
instance), as shown below.
Note that the first parameter of the lambda is the std::stop_token
which will be automatically passed from the std::jthread
. It is then passed "manually" to your Testing::update
method.
#include <iostream>
#include <chrono>
#include <thread>
class Testing {
int updates{ 0 };
public:
void update(std::stop_token s, int i, char l) {
while (!s.stop_requested()) {
updates++;
std::cout << updates << " " << l << "\n";
std::this_thread::sleep_for(std::chrono::milliseconds(i));
}
std::cout << l << " stopped\n";
}
};
int main() {
Testing test_1, test_2;
std::jthread t_1([&test_1](std::stop_token s) { test_1.update(s, 2300, 'A'); });
std::jthread t_2([&test_2](std::stop_token s) { test_2.update(s, 700, 'B'); });
std::this_thread::sleep_for(std::chrono::milliseconds(5000));
std::cout << "Main thread done\n";
t_1.request_stop();
if (t_1.joinable()) { t_1.join(); }
t_2.request_stop();
if (t_2.joinable()) { t_2.join(); }
}
A side-note:
A std::jthread
does not have to be join
ed manually (although it is not wrong).
It is automatically requested to stop and then join
ed in the std::jthread
destructor:
If *this has an associated thread (joinable() == true), calls request_stop() and then join().
Therefore the last 4 lines in the code above are not really needed.
Live demo 2