我一直在尝试更好地理解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;
}
对我来说,用g++
链接的-pthread
编译时,我应该得到以下输出:
1
2
3
4
,因为cout
调用按该顺序进行。但是,输出不一致。 1和2总是按顺序打印,但有时3和4会省略或打印两次。 即12
,123
,1234
或12344
我的工作原理是,主线程在工作线程开始工作或完成之前退出,从而导致输出的遗漏。我可以立即想到一个解决此问题的方法,即创建一个全局布尔变量来表示工作线程何时完成,即主线程在退出之前等待状态更改。这样可以缓解问题。
但是,在我看来,这是一种非常凌乱的方法,可能有更干净的解决方案,尤其是对于这样的问题,它可能经常在线程中出现。
答案 0 :(得分:2)
仅提供一些一般性建议,既适用于在C ++中使用原始pthread,又适用于std :: thread中包装的pthread:获得可读,可理解和可调试行为的最佳方法是使线程同步和生命周期管理明确化。即避免使用pthread_kill,pthread_cancel,并且在大多数情况下,避免分离线程,而应进行显式联接。
我喜欢的一种设计模式是使用std原子标志。当主线程想要退出时,它将原子标志设置为true。工作线程通常在一个循环中完成其工作,并合理地经常检查原子标志,例如每圈一次。当他们发现主要人员命令他们退出时,他们清理并返回。然后,主线程与所有工作人员加入:s。
在某些特殊情况下,需要格外小心,例如,当一个工作人员陷入阻塞的系统调用和/或C库函数中时。通常,该平台提供了摆脱这种阻塞呼叫的方法,而无需借助例如pthread_cancel,因为线程取消在C ++中非常糟糕。如何避免阻塞的一个示例是getaddrinfo_a的Linux联机帮助页,即异步网络地址转换。
另一种不错的设计模式是工人睡觉时,例如选择()。然后,您可以在main和worker之间添加一个额外的控制管道。 Main通过send()向工作人员发出退出信号:在管道上占用一个字节,因此如果工人在select()中睡眠,则将其唤醒。
答案 1 :(得分:1)
如何完成此操作的示例:
#include <functional>
#include <iostream>
#include <thread>
class Test {
std::thread worker; // worker is now a member
public:
Test() { x = 5; } // worker deliberately left without a function to run.
~Test()
{
if (worker.joinable()) // worker can be joined (act was called successfully)
{
worker.join(); // wait for worker thread to exit.
// Note destructor cannot complete if thread cannot be exited.
// Some extra brains needed here for production code.
}
}
void act() {
std::cout << "1" << std::endl;
worker = std::thread(&Test::changex, this); // give worker some work
std::cout << "2" << std::endl;
}
// rest unchanged.
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;
} // test destroyed here. Destruction halts and waits for thread.