2012-01-21 5 views
4

私はLinuxで書いたコードで奇妙な動作を見ましたが、誰かがその原因を知っているかどうかを知りたいと思っています。 私は基底クラスと派生クラスを持っていました。基本クラスでは、仮想メソッドを定義し、派生クラスで同じシグネチャでそのメソッドを再定義しました。その後、スレッドを起動するためにboostを使用しました。ここではサンプルコードです:私はこれをした派生クラスのInitメソッドで仮想関数とブーストバインドの奇妙な振る舞い

Class Base { 
public: 
    virtual void DoSomething(); 
    virtual void Init() = 0; 
    ... 
} 

Class Derived : public Base { 
public: 
    void DoSomething(); 
    void Init(); 
    ... 
} 

:同じ方法ながら

boost::thread *t = new boost::thread(boost::bind(&Base::DoSomething, this)); 

基底クラスのdoSomethingの方法は、それが行うことに意味されたものでした派生クラスの空のメソッドは、間違ってそこに残しました。上のコードを実行している間、ほとんどの場合、基本クラスのDoSomethingがスレッドで実行されたため、アプリケーションは正常に動作しましたが、時には機能しませんでした。いくつかのデバッグの後、私は上記の間違いに気付き、派生クラスのDoSomethingを削除することで問題が解決されました。 Eclipseをデバッグモードで使用すると、派生クラスのDoSomethingメソッドが常に呼び出されたように見えますが、コンソールからアプリケーションを実行するのは大抵の場合、常にそうではありませんでした。この行動の理由はありますか?つまり、なぜバインド関数が基本クラスのメソッドを使用するのはなぜですか、時には派生クラスの同じメソッドを使用するのはなぜですか?完全な実施例を示すことは難しいだろう

を@pmrに応答して、事前

編集中

おかげで、私はクラスがどのように使用されるかビットを表示しようとします。

最初に私はDerivedオブジェクトをインスタンス化し、次にinit関数で上記の初期化コードでスレッドを開始します。 DoSomethingには、ベクトルを反復するwhileループがありますが、それは私が思うところではありません。

void Derived::Init() 
{ 
    ... 
    boost::thread *t = new boost::thread(boost::bind(&Base::DoSomething, this)); 
} 

void Base::DoSomething() 
{ 
    while(true) { 
     ... 
    } 
} 

void Derived::DoSomething() 
{ 
} 

あなたは、このコードで派生doSomethingの方法を見ることができるようには空だったので、時々私の代わりにベースdoSomethingの機能で行われた任意の処理を、表示されませんでした。

+0

最小限のコンパイル可能な例を提供してください。あるいは、クラスがどのように使用されているかを少なくとも示してください。 – pmr

答えて

2

私はこの動作の理由を知ったと思います。最初は、基本コンストラクタの内部にスレッドコンストラクタを呼び出しました。これは問題だと思う。なぜなら、基本コンストラクタが派生オブジェクトの前に呼び出されたため、vtableが空の派生関数を指すように作成されていて、vtableが作成される前にスレッドが開始されていたため、それはそれが意味することをしました。私はデバッグを使用するといくつかの遅延が導入されたと思います。そのため、デバッガを使用すると、スレッドは常に派生クラスメソッドにバインドされ、間違った動作が発生します。また、init関数内でスレッド作成を移動しようとしました。そのようにして、派生した関数が常に呼び出されます。

4

ここでは野生の推測です:実際にスレッドを開始するために使用したオブジェクトが破壊されました!仮想関数の束縛は破壊中に変化するので(オブジェクトが破棄されると、すべての仮想関数は、使用されているオブジェクトが現在破棄されているクラスの型であるかのように解決されます)。これを行うために、「vtableポインタ」は通常、適切な「仮想関数テーブル」を指すようにリセットされる。ベースが破壊されると、オブジェクトをさらに破壊する必要はありません。

これは、ランダムな動作の説明にうまく収まります。親スレッドがベースクラスのコンストラクタに到達するのに十分速く実行されることがあります。デバッグモードでコンパイルすると、親スレッドはオブジェクトを破壊するまでに一貫して長い時間がかかりました。多くの場合、すべてが正常に機能したという文章は、実際にはイメージを破壊しません。たいていの場合、バグのあるコードは動作しているかのように見えますが、

+0

こんにちは、私は、クラスが破壊されないので、私はスレッド初期化の後に同じクラスの他の関数を使用しているので、この場合だとは思わない(これはデーモンの一種ですので、 。 – cpl

0

私たちの内部OSで同じ問題が発生しました。オブジェクトを構築するときに、仮想関数を別のワーカースレッドにバインドします。 OSがクラスの構築関数を導出する前にワーカースレッドに切り替えると、ワーカーは「this」を基本クラス型で呼び出します。だから私はそれを記述することができると思う: "this"ポインタはコンストラクタとDe-constructorでスレッドセーフではない。