gpt4 book ai didi

c++ - 处理 std::thread 包装器类的立即销毁

转载 作者:行者123 更新时间:2023-11-30 01:40:06 25 4
gpt4 key购买 nike

我正在为 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/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com