我一直在努力加深对 C++ 线程的理解,为此我编写了以下示例:
#include <functional>
#include <iostream>
#include <thread>
class Test {
public:
Test() { x = 5; }
void act() {
std::cout << "1" << std::endl;
std::thread worker(&Test::changex, this);
worker.detach();
std::cout << "2" << std::endl;
}
private:
void changex() {
std::cout << "3" << std::endl;
x = 10;
std::cout << "4" << std::endl;
}
int x;
};
int main() {
Test t;
t.act();
return 0;
}
对我来说,当使用 -pthread
链接的 g++
编译时,我应该得到以下输出:
1
2
3
4
因为 cout
调用的顺序是这样的。但是,输出不一致。 1 和 2 总是按顺序打印,但有时 3 和或 4 会被省略或重复打印。 即12
、123
、1234
或 12344
我的工作原理是主线程在工作线程开始工作或完成之前退出,从而导致输出遗漏。我可以立即想到解决这个问题的方法,创建一个全局 bool 变量来表示工作线程何时完成,主线程在退出前等待状态更改。这缓解了这个问题。
但是,对我来说,这感觉像是一种非常困惑的方法,可能有一个更干净的解决方案,尤其是对于线程中可能经常出现的此类问题。
最佳答案
只是一些一般性建议,适用于在 C++ 中使用原始 pthread 和包装在 std::thread 中的 pthread:获得可读、可理解和可调试行为的最佳方法是使线程同步和生命周期管理明确。 IE。避免使用 pthread_kill、pthread_cancel,并且在大多数情况下,避免分离线程,而是进行显式连接。
我喜欢的一种设计模式是使用标准原子标记。当主线程想要退出时,它将原子标志设置为 true。工作线程通常在一个循环中完成它们的工作,并经常检查原子标志,例如每圈循环一次。当他们发现 main 命令他们退出时,他们清理并返回。主线程然后加入:s 与所有 worker 。
有一些特殊情况需要格外小心,例如,当一名工作人员卡在阻塞的系统调用和/或 C 库函数中时。通常,该平台提供了摆脱此类阻塞调用的方法,而无需求助于例如pthread_cancel,因为线程取消在 C++ 中的效果非常差。如何避免阻塞的一个示例是 getaddrinfo_a 的 Linux 联机帮助页,即异步网络地址转换。
另一个不错的设计模式是当员工 sleep 时,例如选择()。然后,您可以在 main 和 worker 之间添加一个额外的控制管道。 Main 通过 send() 向 worker 发出退出信号:在管道上发送一个字节,从而在 select() 中休眠时唤醒 worker。
关于c++ - 调用 std::thread.detach 时的意外行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54952410/