继承,后台线程和RAII

时间:2017-07-18 08:01:03

标签: c++ c++11 raii stdthread

我有一个可以启动后台线程的基类,并在需要时停止它。该线程调用两个虚拟方法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();
  }
}

1 个答案:

答案 0 :(得分:1)

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

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

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