2017-07-18 57 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方法。