2017-07-18 59 views
0

我有一个基类可以启动后台线程,并在需要时停止它。该线程调用两个虚拟方法Open()Close()。所以所有继承的类都可以重新实现这个方法,但不能启动/停止线程例程(比示例更困难)。我想遵循RAII原则,并在基类的构造函数/析构函数中启动/停止线程。继承,后台线程和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

你可以在析构函数里调用'Stop()',你应该是安全的。 – rozina

+0

如果我在构造函数中调用'Start()'而在析构函数中调用Stop(),则输出将为: 继承的开放 基本关闭 –

+0

实际上,基类“Close”和“Open”是纯虚拟的。如果我在基类析构函数中调用'Stop()',程序将会崩溃,使用'Pure virtual method called'错误 –

回答

1

问题与线程无关。如果在Base的构造函数中调用虚方法,则尚未创建Inherited对象,因此调用方法的Base实现(或者如果它们是纯虚拟的,则会出现错误)。如果在Base的析构函数中调用虚拟方法,那么Inherited对象已被销毁,因此再次调用虚拟方法的Base版本。

从另一个线程调用方法不会改变这种行为。但是,线程的启动可能比构造Inherited对象花费的时间更长,因此对象已完全构建,并且在工作线程的开始处调用Inherited方法。

一个解决方案是将RAII移动到另一个对象。因此,在Base的构造函数和析构函数中不要调用StartStop。然后,您可以构建一个StartStopThing,其中需要Base(通过引用或指针)并在其构造函数和析构函数中调用StartStop。或者你建立一个StartStopThing模板类,它以Inherited为模板参数,构建一个Inherited对象并调用StartStop方法。