2017-07-18 4 views
0

バックグラウンドスレッドを開始できるベースクラスがあり、必要なときに停止します。そのスレッドは、2つの仮想メソッド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

私は 'スタート()'コンストラクタと 'ストップ(中)を呼び出すと'デストラクタで出力は次のようになります。 –

+0

近い 継承オープン 基本実際に基本クラス 'Close'と' Open'は、仮想的に純粋です。そして、私がBaseクラスのデストラクタで 'Stop()'を呼び出すと、プログラムは 'Pure virtual method called'エラーでクラッシュします。 –

答えて

1

問題はスレッドに依存しません。あなたがBaseのコンストラクタで仮想メソッドを呼び出す場合は法のBaseの実装が呼び出されるので、Inheritedオブジェクトがまだ作成されていない(または、彼らは純粋仮想である場合にエラーが発生します)。 Baseのデストラクタで仮想メソッドを呼び出すと、Inheritedオブジェクトは既に破棄されているので、再び仮想メソッドのBaseバージョンが再度呼び出されます。この動作を変更しません別のスレッドからメソッドを呼び出す

。しかし、スレッドの開始は、Inheritedオブジェクトの構築よりも時間がかかることがあり、そのためオブジェクトは完全に構築され、Inheritedメソッドはワーカースレッドの先頭で呼び出されます。

解決策の1つは、RAIIを別のオブジェクトに移動することです。したがって、StartStopBaseのコンストラクタとデストラクタで呼び出さないでください。その後、Base(ポインタ参照することによりまたはにより)かかり、そのコンストラクタとデストラクタでStartStopを呼び出すStartStopThingを構築することができます。それとも、テンプレート引数としてInheritedを取るStartStopThingテンプレートクラスを構築し、Inheritedオブジェクトを構築し、StartStopメソッドを呼び出します。