ary*_*naq 5 c++ thread-safety vtable c++11
假设我有以下代码
#include <thread>
#include <iostream>
#include <atomic>
struct FooBase {
void start(){
run_condition_ = true;
t_ = std::thread([this](){
thread_handler();
});
}
virtual ~FooBase(){
run_condition_ = false;
if(t_.joinable())
t_.join();
}
protected:
virtual void thread_handler() = 0;
std::atomic_bool run_condition_{false};
private:
std::thread t_;
};
struct Foo : FooBase {
void thread_handler() override {
while(run_condition_){
std::cout << "Foo derived thread.." << std::endl;
}
}
};
int main(){
Foo f;
f.start();
getchar();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
在这里,我认为因为派生类的析构函数在vtable查找发生在基类Foo之前被调用,如果线程在完成析构函数时尚未加入(仍在运行).由于是纯粹的虚拟我基本上保证了sigabort.FooBasethread_handlerFooFooBase::thread_handler
我该如何防范这个?我没有thread_handler像纯虚拟那样破解我的方式
virtual void thread_handler(){}
Run Code Online (Sandbox Code Playgroud)
但我很遗憾在基类本身如何防范这种情况,我可以在基类中实现一个join_thread接口并从每个派生类中调用它,但这看起来很麻烦.
这里有两个问题,两者都与您所描述的不完全匹配。
你的线程只会在 中停止~FooBase()。这意味着,如果Foo::thread_handler对其任何成员进行读取或写入,它们将在线程停止之前从其下方被销毁。
如果您足够快地到达析构函数,则在被销毁时可能start()不会在新线程上实际调用- 这将导致纯虚拟调用。thread_handler()Foo
无论哪种方式,您都需要确保在Foo销毁时,所有相关的事情thread_handler都已完成。这意味着每个派生类都FooBase必须在其析构函数中具有:
run_condition_ = false;
if (t_.joinable()) {
t_join();
}
Run Code Online (Sandbox Code Playgroud)
抛开这直接不起作用,因为t_(private您可以将其包装到protected stop()),如果所有派生类都需要做一些特殊的事情才能工作,那么这是一个尴尬的设计。您可以放入FooBase它自己的类中,该类只接受任意可调用对象作为参数:
class joining_thread {
public:
joining_thread() = default;
~joining_thread() { stop(); }
bool running() const { return run_condition_.load(); }
template <typename... Args>
void start(Args&&... args) {
run_condition_ = true;
t_ = std::thread(std::forward<Args>(args)...);
}
void stop() {
run_condition_ = false;
if (t_.joinable()) t_.join();
}
private:
std::thread t_;
std::atomic_bool run_condition_{false};
};
Run Code Online (Sandbox Code Playgroud)
然后你就Foo可以成为会员了:
class Foo {
public:
void start() {
t_.start([this]{
while (t_.running()) { ... }
});
}
private:
// just make me the last member, so it's destroyed first
joining_thread t_;
};
Run Code Online (Sandbox Code Playgroud)
整个事情仍然有点尴尬running(),但希望这个想法是有意义的。