2015-11-16 112 views
2

我正在尋找一個基類中的線程,該基類不斷調用被派生類覆蓋的類中的純虛方法。帶純虛擬回調的螺紋基類,停止銷燬C++

對於啓動線程,我沒有問題,因爲我可以在構建完成後調用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(); 
}; 
+0

在任何情況下,您都將在派生對象和基礎上進行多態處理。所以,掛起線程的邏輯必須從Derived類析構函數中調用。實現懸架/螺紋閉合的實際功能可以在Base類中實現。 – Arunmu

+0

你應該重新考慮你的設計。你的派生類至少有兩個職責。線程控制繼承自基礎和您的算法來完成這項工作。你應該把它分開。將派生類通過接口傳遞給Thread類。這將避免這個問題。 – mkaes

+0

我認爲你的設計/策略是不正確的,但它看起來不正確。 – Arunmu

回答

1

我不認爲你將能夠避免重複的號召,加入線程在每個派生類的析構函數。如果一個線程依賴於一個非靜態對象o上,那麼它是一個好主意,有一個明確的所有權關係,以保證對象的有效性:

  1. 線程應該擁有oo破壞將被處理通過線程對象的析構函數,在加入之後。
  2. o應該擁有該線程並且應該在其自身的析構函數中加入該線程。

除了線程依賴於派生對象,您已經選擇了第二種方法,但派生對象並不直接擁有線程,而是通過子對象(基礎對象)。由於線程依賴於派生對象,因此必須將其連接到派生對象的析構函數中。

0

你應該分開兩個行爲:一個類來運行和加入線程,這個函數層次結構的基類。

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

我的建議是使用了weak_ptr知道當對象的生命週期已經結束:

  1. 工廠實例化(衍生)對象並將其存儲在一個shared_ptr
  2. 工廠實例化看門狗類並將它傳遞給新對象weak_ptr
  3. 看門狗線程現在可以檢查弱指針是否每次需要訪問它時都會過期。當它到期時,線程將自行終止。

下面是一個例子(而不是一個工廠,我只是用主):

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

注意,沒有必要明確地停止線程,因爲它會自行停止,只要它檢測到物體的壽命已經結束。另一方面,請注意線程可能略微超過被監控對象的生命週期,這可能被認爲是不好的設計(它可能會延遲程序終止)。我想可以通過在基類析構函數中加入線程,在發信號表示它應該終止(如果尚未終止的話)之後加以解決。