std::jthread从另一个成员函数运行成员函数



这是我的代码:

#include <iostream>
#include <zconf.h>
#include <thread>
class JT {
public:
std::jthread j1;
JT() {
j1 = std::jthread(&JT::init, this, std::stop_token());
}
void init(std::stop_token st={}) {
while (!st.stop_requested()) {
std::cout << "Hello" << std::endl;
sleep(1);
}
std::cout << "Bye" << std::endl;
}
};
void init_2(std::stop_token st = {}) {
while (!st.stop_requested()) {
std::cout << "Hello 2" << std::endl;
sleep(1);
}
std::cout << "Bye 2" << std::endl;
}
int main() {
std::cout << "Start" << std::endl;
JT *jt = new JT();
std::jthread j2(init_2);
sleep(5);
std::cout << "Finish" << std::endl;
}

这是输出:

Start
Hello
Hello 2
Hello
Hello 2
Hello
Hello 2
Hello
Hello 2
Hello
Hello 2
Finish
Bye 2
Hello

问题是我可以得到Bye 2消息,但不能得到Bye消息。

我知道传递的stop_token变量会导致这个问题,但我不知道如何将它传递给另一个成员函数中的成员函数。

如果我正确地理解了这个问题(我的理解是,对于std::jthread(&JT::init, this)jthread想要调用JT::init(std::stop_token st, this),这是不起作用的(,您可能想要使用std::bind_front来给它一个起作用的Callable。例如

JT() {
j1 = std::jthread(std::bind_front(&JT::init, this));
}

根据有用的注释,我将类代码重写如下:

class JT {
public:
std::jthread j1;
JT() {
j1 = std::jthread(&JT::init, this);
}
void init() {
auto st = j1.get_stop_token();
while (!st.stop_requested()) {
std::cout << "Hello" << std::endl;
sleep(1);
}
std::cout << "Bye" << std::endl;
}
};

您必须通过auto st = j1.get_stop_token();获得飞行途中的停留时间。

以及修改后的主要功能:

int main() {
std::cout << "Start" << std::endl;
JT *jt = new JT();
//    auto jt = std::make_unique<JT>();
std::jthread j2(init_2);
sleep(5);
std::cout << "Finish" << std::endl;
delete jt;
}

您需要直接delete类对象,或者使用RAII(类似于智能指针(。

在线程构造过程中,JT::init函数必须接收std::stop_token作为参数。您可以使用std::bind

j1 = std::jthread{ std::bind(&JT::init, this, std::placeholders::_1) };

或者,更简单地说,如@Hasturkun答案中的std::bind_front

注意在如下构造线程后获得std::stop_token会导致竞争条件,因为行j1 = std::jthread(&JT::init, this);涉及构造函数和移动运算符,第二个运算符与行auto st = j1.get_stop_token();一致,如下所示:

#include <thread>
#include <iostream>
using namespace std::chrono_literals;
class JT {
public:
std::jthread j1;
JT() {
j1 = std::jthread(&JT::init, this);
}
~JT() {
j1.request_stop();
j1.join();
}
void init() {
auto st = j1.get_stop_token();
while (!st.stop_requested()) {
std::this_thread::sleep_for(1ms);
std::cout << "Hello" << std::endl;
}
std::cout << "Bye" << std::endl;
}
};
int main() {
std::cout << "Start" << std::endl;
for (int i = 0; i < 1000; i++) {
JT jt;
std::this_thread::sleep_for(5ms);
}
}

结果是:

Start
Hello
Bye
Hello
Bye
Hello
Hello
Hello
Hello
Hello
Hello
....

节目永远不会结束。我已经用gcc 12.1.0和msvc(VS 2019 16.11.5(进行了发布测试。

这可以通过使用构造函数初始值设定项列表来修复,该列表不会调用移动运算符:

JT() : j1(std::jthread(&JT::init, this)) {
}

最新更新