我正在尝试为std :: thread创建一个包装类。此类提供kick方法,该方法启动线程并调用纯虚函数。我使用派生类来调用此kick方法,派生类也实现了虚函数。
class Executor
{
public:
// constructor
Executor();
// destructor
~Executor();
// kick thread execution
void Kick();
private:
// thread execution function
virtual void StartExecution() = 0;
// thread handle
std::thread mThreadHandle;
};
以下是执行者类
的实现Executor::Executor()
{
// Nothing to be done here
}
Executor::~Executor()
{
if (mThreadHandle.joinable())
mThreadHandle.join();
}
void Executor::Kick()
{
// mThreadHandle = std::thread(&Executor::StartExecution, this);
mThreadHandle = std::thread([this] {this->StartExecution();});
}
我正在使用 Consumer 类继承此类并实现StartExecution方法。当我使用kick方法时,它显示调用的纯虚函数,程序终止。
std::unique_ptr<Consumer> consumer = std::make_unique<Consumer>();
consumer->Kick();
在执行者踢法中。我添加了断点并开始查找错误。
mThreadHandle = std :: thread([this] {this-&gt; StartExecution();});
两次。首先是因为kick方法,所以第二个执行lambda函数。我第一次看到这个指向消费者类。但是当谈到lambda函数时,它会搞砸, vptr 指向纯虚函数。
我会对这里的错误感兴趣而不是简单的答案。
答案 0 :(得分:3)
根据我的尝试进行猜测:在线程执行之前,Consumer
被破坏。
我已经~Executor
虚拟并为相关的函数调用添加了一些打印语句。
#include <iostream>
#include <memory>
#include <thread>
#include <chrono>
class Executor
{
public:
// constructor
Executor();
// destructor
virtual ~Executor();
// kick thread execution
void Kick();
private:
// thread execution function
virtual void StartExecution() { std::cout << "Executor::Kick\n"; }
// thread handle
std::thread mThreadHandle;
};
Executor::Executor()
{
// Nothing to be done here
}
Executor::~Executor()
{
std::cout << "~Executor\n";
if (mThreadHandle.joinable())
mThreadHandle.join();
}
void Executor::Kick()
{
// mThreadHandle = std::thread(&Executor::StartExecution, this);
mThreadHandle = std::thread([this] {this->StartExecution();});
}
class Consumer: public Executor {
public:
~Consumer() {
std::cout << "~Consumer\n";
}
private:
virtual void StartExecution() { std::cout << "Consumer::Kick\n"; }
};
int main() {
{
std::cout << "1:\n";
std::unique_ptr<Consumer> consumer = std::make_unique<Consumer>();
consumer->Kick();
}
{
std::cout << "2:\n";
std::unique_ptr<Consumer> consumer = std::make_unique<Consumer>();
consumer->Kick();
std::cout << "Sleeping for a bit\n";
std::this_thread::sleep_for(std::chrono::seconds(1));
}
return 0;
}
输出:
1:
~Consumer
~Executor
Executor::Kick
2:
Sleeping for a bit
Consumer::Kick
~Consumer
~Executor
在销毁消费者之前休眠让线程运行并调用正确的函数。 “真正的”解决方案是确保消费者至少与线程本身一样长。由于线程存在于基类Executor
中,因此无法保证,因为派生类在基类之前被破坏。
来自cppreference(强调我的):
当从构造函数或析构函数直接或间接调用虚函数时(包括在构造或销毁类的非静态数据成员期间,例如在成员初始化程序列表中),以及调用的对象apply是正在构造或销毁的对象,被调用的函数是构造函数或析构函数类中的最终覆盖,而不是在更派生的类中覆盖它。 换句话说,在构造或破坏期间,不存在更多派生的类。
当在构造/销毁期间在不同的线程中调用成员函数时,这似乎适用。
答案 1 :(得分:1)
即使在线程有机会调用该函数之前,程序也会内存不足。如果你改变你的代码
void Executor::Kick()
{
mThreadHandle = std::thread([this] {this->StartExecution();});
this_thread::sleep_for(chrono::seconds(1)); // any number
}
这会奏效。
这就是为什么你不能通过捕获列表
中的引用传递this
的确切原因
现在谈谈您的具体问题
我会对这个问题感兴趣而不是简单的答案。
vPTR指向VTable,当类离开内存时,vPTR指向基类VTable,因此发生了这种情况。您可以在调用函数
之前打印vTable地址来检查相同内容