继承、后台线程和RAII
Inheritance, background thread and RAII
我有一个基础 class 可以启动后台线程,并在需要时停止它。该线程调用两个虚拟方法 Open()
和 Close()
。所以所有继承的 classes 都可以重新实现这个方法,但不是 starting/stoping 线程例程(它比示例中的更难)。我想遵循 RAII 原则和 start/stop thid thread in constructor/destructor of base class.
问题是,在 constructor/destructor 中调用虚拟方法是一种不好的做法,在我的情况下不起作用。
这是我的问题的示例:
#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();
}
}
问题与线程无关。如果您在 Base
的构造函数中调用虚方法,则 Inherited
对象尚未创建,因此会调用这些方法的 Base
实现(如果它们是纯方法,您会得到一个错误虚拟的)。如果您在 Base
的析构函数中调用虚方法,则 Inherited
对象已经被销毁,因此再次调用 Base
版本的虚方法。
从另一个线程调用方法不会改变此行为。但是线程的启动可能比 Inherited
对象的构造花费更长的时间,因此对象被完全构造并且 Inherited
方法在工作线程的开始被调用。
一个解决方案是将 RAII 移动到另一个对象。所以你不要在Base
的构造函数和析构函数中调用Start
和Stop
。然后你可以构建一个 StartStopThing
,它接受一个 Base
(通过引用或通过指针)并在其构造函数和析构函数中调用 Start
和 Stop
。或者您构建一个 StartStopThing
模板 class,它将 Inherited
作为模板参数,构建一个 Inherited
对象并调用 Start
和 Stop
方法。
我有一个基础 class 可以启动后台线程,并在需要时停止它。该线程调用两个虚拟方法 Open()
和 Close()
。所以所有继承的 classes 都可以重新实现这个方法,但不是 starting/stoping 线程例程(它比示例中的更难)。我想遵循 RAII 原则和 start/stop thid thread in constructor/destructor of base class.
问题是,在 constructor/destructor 中调用虚拟方法是一种不好的做法,在我的情况下不起作用。 这是我的问题的示例:
#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();
}
}
问题与线程无关。如果您在 Base
的构造函数中调用虚方法,则 Inherited
对象尚未创建,因此会调用这些方法的 Base
实现(如果它们是纯方法,您会得到一个错误虚拟的)。如果您在 Base
的析构函数中调用虚方法,则 Inherited
对象已经被销毁,因此再次调用 Base
版本的虚方法。
从另一个线程调用方法不会改变此行为。但是线程的启动可能比 Inherited
对象的构造花费更长的时间,因此对象被完全构造并且 Inherited
方法在工作线程的开始被调用。
一个解决方案是将 RAII 移动到另一个对象。所以你不要在Base
的构造函数和析构函数中调用Start
和Stop
。然后你可以构建一个 StartStopThing
,它接受一个 Base
(通过引用或通过指针)并在其构造函数和析构函数中调用 Start
和 Stop
。或者您构建一个 StartStopThing
模板 class,它将 Inherited
作为模板参数,构建一个 Inherited
对象并调用 Start
和 Stop
方法。