【问题标题】:Threaded base class with pure virtual callback, stopping on destruction c++具有纯虚拟回调的线程基类,在销毁 C++ 时停止
【发布时间】:2016-02-17 15:22:37
【问题描述】:

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

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

但是,由于类的生命周期由 shared_ptr 管理,因此我无法调用类似的方法来停止线程。如果我在析构函数中停止线程,它将导致段错误,因为派生类在基类之前被销毁,因此会尝试调用不存在的函数。

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

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

例子:

#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();
};

【问题讨论】:

  • 在任何情况下,您都将在派生对象上工作,并在 Base 上进行多态工作。因此,挂起线程的逻辑必须从 Derived 类析构函数中调用。实现暂停/线程关闭的实际功能可以在基类中实现。
  • 你应该重新考虑你的设计。您的 DerivedClasses 至少有两个职责。从基础继承的线程控制和你的算法来做这项工作。你应该把这个分开。通过接口将派生类传递给 Thread 类。这样可以避免问题。
  • 我认为您的设计/策略不正确,只是看起来不正确。

标签: c++ multithreading boost


【解决方案1】:

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

  1. 线程应该拥有oo 的销毁将在加入后由线程对象的析构函数处理。
  2. o 应该拥有线程并且应该在它自己的析构函数中加入线程。

您选择了第二种方法,除了线程依赖于派生对象,但派生对象不直接拥有线程,而是通过子对象(基础对象)。由于线程依赖于派生对象,所以它必须加入派生对象的析构函数中。

【讨论】:

    【解决方案2】:

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

    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;
    };
    

    【讨论】:

      【解决方案3】:

      我的建议是使用 weak_ptr 来了解对象的生命周期何时结束:

      1. 工厂实例化(派生)对象并将其存储在 shared_ptr 中
      2. 工厂实例化看门狗类并将其传递给新对象的weak_ptr
      3. 看门狗线程现在可以在每次需要访问弱指针时检查它是否已过期。当它过期时,线程将自行终止。

      这里是一个例子(不是工厂,我只是用了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;
      }
      

      请注意,无需显式停止线程,因为一旦检测到对象的生命周期结束,它就会自行停止。另一方面,请注意线程可能会稍微超过被监视对象的生命周期,这可能被认为是糟糕的设计(例如,它可能会延迟程序终止)。我想可以通过加入基类析构函数中的线程来解决这个问题,在发出信号应该终止(如果尚未终止)之后。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-05-23
        • 2010-10-15
        • 2012-08-02
        • 2012-11-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-09
        相关资源
        最近更新 更多