我有一个可以启动后台线程的基类,并在需要时停止它。该线程调用两个虚拟方法Open()
和Close()
。所以所有继承的类都可以重新实现这些方法,但不能启动/停止线程例程(比例子更难)。我想遵循RAII原则并在基类的构造函数/析构函数中启动/停止该线程。
问题是,在构造函数/析构函数中调用虚方法是一种不好的做法,在我的情况下不起作用。 以下是我的问题的一个例子:
#include <iostream>
#include <thread>
#include <atomic>
class Base {
public:
Base() {
bg_thread_ = std::thread([this] {
Open();
while(!is_stop_) {
// do stuff
}
Close();
});
}
~Base() {
is_stop_ = true;
if(bg_thread_.joinable()) {
bg_thread_.join();
}
}
private:
virtual void Open() {
std::cout << "Base open" << std::endl;
}
virtual void Close() {
std::cout << "Base close" << std::endl;
}
std::thread bg_thread_;
std::atomic<bool> is_stop_{false};
};
class Inherited : public Base {
virtual void Open() override {
std::cout << "Inherited open" << std::endl;
}
virtual void Close() override {
std::cout << "Inherited close" << std::endl;
}
};
int main() {
Inherited inherited;
std::this_thread::sleep_for(std::chrono::seconds(1));
return 0;
}
输出结果为:
Inherited open
Base close
没有睡觉就是:
Base open
Base close
我目前的方法是在构造函数之后调用Start()
方法,在析构函数之前调用Stop()
,但我想用RAII解决。
void Start() {
bg_thread_ = std::thread([this] {
Open();
while(!is_stop_) {
// do stuff
}
Close();
});
}
void Stop() {
is_stop_ = true;
if(bg_thread_.joinable()) {
bg_thread_.join();
}
}
答案 0 :(得分:1)
问题与线程无关。如果在Base
的构造函数中调用虚方法,则尚未创建Inherited
对象,因此调用方法的Base
实现(如果它们是纯的,则会出错虚拟)。如果在Base
的析构函数中调用虚方法,则Inherited
对象已被销毁,因此再次调用Base
版本的虚拟方法。
从另一个线程调用方法不会改变此行为。但是线程的启动可能比构造Inherited
对象花费的时间更长,因此对象是完全构造的,并且在工作线程的开头调用Inherited
方法。
一种解决方案是将RAII移动到另一个对象。因此,您不能在Start
的构造函数和析构函数中调用Stop
和Base
。然后,您可以构建一个StartStopThing
,它接受Base
(通过引用或指针)并在其构造函数和析构函数中调用Start
和Stop
。或者,您构建一个StartStopThing
模板类,以Inherited
作为模板参数,构建Inherited
对象并调用Start
和Stop
方法。