具有纯虚拟回调的线程基类,在销毁c ++时停止

时间:2015-11-16 13:46:11

标签: c++ multithreading boost

我希望在基类中运行一个线程,该基类不断调用该类中的纯虚方法,并被派生类覆盖。

为了启动线程,我没有问题,因为我可以在构建之后调用HasInitalized()函数。因此,在完全构造类之后启动线程。

然而,作为班级'生命周期由shared_ptr管理,我无法调用类似的方法来停止线程。如果我在析构函数中停止线程,它将导致seg-fault,因为派生类在基类之前被销毁,因此将尝试调用不存在的函数。

我知道我可以从派生类调用一个停止函数,但不想在派生类的每个实例上调用它。

有没有办法解决这个问题。

示例:

#include "boost/thread.hpp"

class BaseClass
{
public:
  BaseClass()
  {
  }

  // Start the thread
  void Start()
  {
    _thread = boost::thread(&BaseClass::ThreadLoop, this);
  }

  virtual ~BaseClass()
  {
    _thread.interrupt();
    _thread.join();
  }

private:

  // Will loop until thread is interupted
  void ThreadLoop()
  {
    try
    {
      while(true)
      {
        DoSomethingInDerivedClass();
        boost::this_thread::interruption_point();
      }
    }
    catch(...)
    {

    }
  }

  boost::thread _thread;

protected:

  virtual void DoSomethingInDerivedClass() = 0;
};


class DerivedClass : public BaseClass
{

  DerivedClass()
  {
  }

  ~DerivedClass()
  {
    // This gets called before base class destructor.
  }

protected:

  void DoSomethingInDerivedClass();
};

3 个答案:

答案 0 :(得分:3)

我不认为您将能够避免重复调用以在每个派生类的析构函数中加入线程。如果一个线程依赖于一个非静态对象o,那么建立一个明确的所有权关系以保证该对象的有效性是个好主意:

  1. 线程应该拥有o,并且在加入之后,线程对象的析构函数将处理o的销毁。
  2. o应该拥有该主题,并且应该将该主题加入其自己的析构函数中。
  3. 您选择了第二种方法,但线程依赖于派生对象,但派生对象并不直接拥有线程,而是通过子对象(基础对象)。由于线程依赖于派生对象,因此它必须在派生对象的析构函数中连接。

答案 1 :(得分:1)

您应该将这两种行为分开:运行和加入线程的类,即功能层次结构的基类。

class Runner {
public:
    explicit Runner(std::shared_ptr<BaseClass> ptr) : m_ptr(ptr) {
        m_thread = boost::thread(&Runner::ThreadLoop, this);
    }
    ~Runner() {
        m_thread.interrupt();
        m_thread.join();
    }

private:
    void ThreadLoop() {
        try {
            while(true) {
                m_ptr->DoSomethingInDerivedClass();
                boost::this_thread::interruption_point();
            }
        } catch(...) {
        }
    }

    std::shared_ptr<BaseClass> m_ptr;
    std::thread m_thread;
};

答案 2 :(得分:0)

我的建议是使用weak_ptr知道对象的生命周期何时结束:

  1. 工厂实例化(派生)对象并将其存储在shared_ptr
  2. 工厂实例化监视程序类并将weak_ptr传递给新对象
  3. 监视程序线程现在可以在每次需要访问它时检查弱指针是否过期。当它过期时,线程将自行终止。
  4. 这是一个例子(而不是工厂,我只使用了main):

    #include <thread>
    
    class BaseClass
    {
    public:
      virtual ~BaseClass() = default;
      virtual void DoSomethingInDerivedClass() = 0;
    };
    
    class DerivedClass : public BaseClass
    {
    public:
      void DoSomethingInDerivedClass() override {}
    };
    
    // Will loop until weak_base expires
    void ThreadLoop(std::weak_ptr<BaseClass> weak_base)
    {
      try
      {
        while (true)
        {
          std::shared_ptr<BaseClass> base = weak_base.lock();
          if (base) {
            base->DoSomethingInDerivedClass();
          }
          else {
            break; // Base is gone. Terminate thread.
          }
        }
      }
      catch (...)
      {
      }
    }
    
    int main()
    {
      std::shared_ptr<DerivedClass> obj = std::make_shared<DerivedClass>();
      std::thread([&] { ThreadLoop(obj); }).detach();
      return 0;
    }
    

    请注意,不需要显式停止线程,因为一旦检测到对象的生命周期结束,它就会自动停止。另一方面,请注意,线程可能略微超过被监视对象的生命周期,这可能被认为是糟糕的设计(例如,它可以推迟程序终止)。我想通过联系基类析构函数中的线程,在发出信号表明它应该终止(如果还没有终止)之后,可以解决这个问题。