- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在为 std::thread 编写面向对象的包装器。代码的简化版本如下所示。这个类的问题是,如果它立即被销毁,它可能会抛出错误,因为在类被销毁的同时从线程调用了 doWork(调用纯虚拟方法)。
测试用例显示在底部。
如何让这个类更安全?如果 MyConThread 具有从 MyConThread::doWork 使用的成员变量,则更复杂的示例会更糟。
我意识到我在启动时遇到了类似的问题,在构造派生类之前可以调用 doWork。
#include <thread>
class ConThread {
public:
ConThread ()
:t_ (doWorkInternal, this)
{}
~ConThread ()
{
if (t_.joinable()) {
t_.join();//avoid a crash because std::thread will terminate the app if the thread is still running in it's destructor
}
}
std::thread& get () {return t_;};
protected:
virtual void doWork ()=0;
private:
static void doWorkInternal (ConThread* t)
{
try {
t->doWork ();
} catch (...)
{};
}
std::thread t_;
};
我遇到的问题与下面的测试用例有关:
class MyConThread: public ConThread
{
public:
long i=0;
protected:
void doWork () override
{
for (long j=0; j<1000000_ && requestedToTerminate_==false; j++)
{
++i;
}
}
};
TEST(MyConThreadTest, TestThatCanBeDestroyed)
{
MyConThread mct (); //<== crashes when being destroyed because thread calls t->doWork ()
}
最佳答案
首先,无论线程对象是否被销毁,你的程序都会崩溃。这很容易检查,只需在创建对象后插入一些延迟即可:
using namespace std::chrono_literals;
TEST(MyConThreadTest, TestThatCanBeDestroyed)
{
MyConThread mct ();
std::this_thread::sleep_for(100s);
}
崩溃的发生是因为您从构造函数中调用虚方法,这通常是一个非常糟糕的主意。基本上,在 C++ 中,对象是按从基到派生的顺序创建的,当您在 ctor 中调用纯虚方法时,还无法处理重载(因为尚未构造派生)。看这个answer
因此,第一条规则:永远不要从构造函数或析构函数中调用虚方法(无论是纯方法还是已定义的方法)。
我认为解决此问题的最简单方法是添加实际启动线程的 start
方法。像这样:
ConThread()
{
}
void start()
{
t_ = std::thread(doWorkInternal, this);
}
一般来说,我不喜欢将逻辑对象和线程对象混合在一起的想法,因为这样做会违反单一职责原则。你的对象做了两件事——它是一个线程,它还有一段你自己的逻辑。通常最好将它们分开处理,这就是为什么 std::thread
提供了通过构造将逻辑“传递”到其中的方法,并且它不是为用作基类而设计的。我找到了一个很好的article关于这一点,它是关于 Qt 线程而不是 std
线程,但概念是相同的。
我通常在我的代码中做什么(这也不理想,但更干净):
std::thread readerThread([]
{
DatasetReader reader;
reader.init();
reader.run();
});
std::thread mesherThread([]
{
Mesher mesher;
mesher.init();
mesher.run();
});
readerThread.join();
mesherThread.join();
如果你想在 dtor 中自动加入你的线程,只需围绕 std::thread
创建一个包装器,但保留将逻辑传递给它的接口(interface)(如 lambda,或函数指针和参数,等等)
关于c++ - 处理 std::thread 包装器类的立即销毁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44090239/
我刚开始使用 Dagger 2,想知道与我目前用来实现依赖注入(inject)的技术相比,它有什么优势。 目前,为了实现 DI,我创建了一个具有两种风格的项目:mock 和 prod。在这些风格中,我
我是一名优秀的程序员,十分优秀!