the*_*dal 5 c++ lambda multithreading c++11 stdthread
我正在尝试为 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;
};
Run Code Online (Sandbox Code Playgroud)
下面是executor类的实现
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();});
}
Run Code Online (Sandbox Code Playgroud)
我正在使用继承这个类并实现 StartExecution 方法的Consumer类。当我使用 kick 方法时,它显示调用的纯虚函数并且程序终止。
std::unique_ptr<Consumer> consumer = std::make_unique<Consumer>();
consumer->Kick();
Run Code Online (Sandbox Code Playgroud)
在 executor kick 方法中。我添加了一个断点并开始寻找问题所在。它来到
mThreadHandle = std::thread([this] {this->StartExecution();});
线两次。首先是因为 kick 方法,其次是执行 lambda 函数。我第一次看到this指向Consumer类。但是当涉及到 lambda 函数时,它就搞砸了,vptr指向纯虚函数。
我会对这有什么问题而不是简单的答案感兴趣。
只是基于我尝试过的猜测:您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;
}
Run Code Online (Sandbox Code Playgroud)
输出:
1:
~Consumer
~Executor
Executor::Kick
2:
Sleeping for a bit
Consumer::Kick
~Consumer
~Executor
Run Code Online (Sandbox Code Playgroud)
在销毁消费者之前休眠让线程运行并调用正确的函数。“真正”的解决方案是确保消费者至少与线程本身一样长。由于线程存在于基类中,Executor因此不能保证,因为派生类在基类之前被破坏。
来自cppreference(强调我的):
当从构造函数或析构函数直接或间接调用虚函数时(包括在类的非静态数据成员的构造或销毁期间,例如在成员初始值设定项列表中),并且该调用适用的对象是正在构造或销毁的对象,被调用的函数是构造函数或析构函数类中的最终覆盖程序,而不是在派生程度更高的类中覆盖它。换句话说,在构造或销毁期间,派生程度更高的类不存在。
这似乎适用于在构造/销毁期间在不同线程中调用成员函数的情况。