天天看點

多線程下vc2003,vc2005對虛函數表處理的BUG?

考慮一下多線程代碼,在設計上,App為了擷取更多的功能,從Window派生,而App同時為了擷取

某個子產品的回調(所謂的Listener),App同時派生Listener,并将自己的指針交給另一個子產品,

另一個子產品通過該指針多态回調到App的實作(對Listener規定的接口的implemention)。設計上

隻是一個很簡單的Listener回調,在單線程模式下一切都很正常(後面我會羅列代碼),但是換到

多線程下,編譯器似乎就對語言機制的支援不夠了:

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

///

多線程下vc2003,vc2005對虛函數表處理的BUG?

/// to demonstrate the fucking bug.

多線程下vc2003,vc2005對虛函數表處理的BUG?

///

多線程下vc2003,vc2005對虛函數表處理的BUG?

#include  < iostream >

多線程下vc2003,vc2005對虛函數表處理的BUG?

#include  < process.h >

多線程下vc2003,vc2005對虛函數表處理的BUG?

#include  < windows.h >

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

class  Window

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

{

多線程下vc2003,vc2005對虛函數表處理的BUG?

public:

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

    /// 

多線程下vc2003,vc2005對虛函數表處理的BUG?

    virtual void wrong()

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

{

多線程下vc2003,vc2005對虛函數表處理的BUG?

        std::cout << "wrong" << std::endl;

多線程下vc2003,vc2005對虛函數表處理的BUG?

    }

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

    virtual ~Window()

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

{

多線程下vc2003,vc2005對虛函數表處理的BUG?

        std::cout << "~Window" << std::endl;

多線程下vc2003,vc2005對虛函數表處理的BUG?

    }

多線程下vc2003,vc2005對虛函數表處理的BUG?

} ;

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

class  Listener

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

{

多線程下vc2003,vc2005對虛函數表處理的BUG?

public:

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

    /// as most listener class, it only put some interface here

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

    virtual void show() 

多線程下vc2003,vc2005對虛函數表處理的BUG?

{}

多線程下vc2003,vc2005對虛函數表處理的BUG?

} ;

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

class  Game

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

{

多線程下vc2003,vc2005對虛函數表處理的BUG?

public:

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

    Game() : _listener( 0 ) 

多線程下vc2003,vc2005對虛函數表處理的BUG?

{ }

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

    void init( Listener *listener )

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

{

多線程下vc2003,vc2005對虛函數表處理的BUG?

        _listener = listener;

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

        /// it will call Window::wrong function but not App::show.

多線程下vc2003,vc2005對虛函數表處理的BUG?

        _listener->show();

多線程下vc2003,vc2005對虛函數表處理的BUG?

    }

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

private:

多線程下vc2003,vc2005對虛函數表處理的BUG?

    Listener *_listener;

多線程下vc2003,vc2005對虛函數表處理的BUG?

} ;

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

Game gGame;

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

static  unsigned  int  __stdcall ThreadFunc(  void   * p )

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

{

多線程下vc2003,vc2005對虛函數表處理的BUG?

    Listener *listener = (Listener*) p;

多線程下vc2003,vc2005對虛函數表處理的BUG?

    gGame.init( listener );

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

    while( true )

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

{

多線程下vc2003,vc2005對虛函數表處理的BUG?

        std::cout << ".";

多線程下vc2003,vc2005對虛函數表處理的BUG?

        Sleep( 100 );

多線程下vc2003,vc2005對虛函數表處理的BUG?

    }

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

    _endthreadex( 0 );

多線程下vc2003,vc2005對虛函數表處理的BUG?

    return 0;

多線程下vc2003,vc2005對虛函數表處理的BUG?

}

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

class  App :  public  Window,  public  Listener

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

{

多線程下vc2003,vc2005對虛函數表處理的BUG?

public:

多線程下vc2003,vc2005對虛函數表處理的BUG?

    void init()

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

{

多線程下vc2003,vc2005對虛函數表處理的BUG?

        // create the game thread

多線程下vc2003,vc2005對虛函數表處理的BUG?

        _game_thread = (HANDLE)_beginthreadex( NULL, 0, ThreadFunc, this, 0, NULL );

多線程下vc2003,vc2005對虛函數表處理的BUG?

    }

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

    /// implement the interface

多線程下vc2003,vc2005對虛函數表處理的BUG?

    void show()

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

{

多線程下vc2003,vc2005對虛函數表處理的BUG?

        std::cout << "App::show" << std::endl;

多線程下vc2003,vc2005對虛函數表處理的BUG?

    }

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

    /// exit

多線程下vc2003,vc2005對虛函數表處理的BUG?

    void exit()

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

{

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

        /// just for testing purpose

多線程下vc2003,vc2005對虛函數表處理的BUG?

        ::TerminateThread( _game_thread, 1 );

多線程下vc2003,vc2005對虛函數表處理的BUG?

        ::CloseHandle( _game_thread );

多線程下vc2003,vc2005對虛函數表處理的BUG?

    }

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

private:

多線程下vc2003,vc2005對虛函數表處理的BUG?

    HANDLE _game_thread;

多線程下vc2003,vc2005對虛函數表處理的BUG?

} ;

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

App gApp;

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

int  main()

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

{

多線程下vc2003,vc2005對虛函數表處理的BUG?

    gApp.init();

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

    std::cout << "Press enter key to exit!" << std::endl;

多線程下vc2003,vc2005對虛函數表處理的BUG?

    std::cin.get();

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

    gApp.exit();

多線程下vc2003,vc2005對虛函數表處理的BUG?

    return 0;

多線程下vc2003,vc2005對虛函數表處理的BUG?

}

多線程下vc2003,vc2005對虛函數表處理的BUG?

App多重繼承Window和Listener,在Game裡回調App::show時,卻調用到了Window::wrong函數。看上去,傳給

Game的Listener指針所指向的虛函數表錯誤了(vtable指針錯了)。App先繼承Listener後繼承Window時,情況

就正确了。(因為使用了_beginthreadex,程式需要連結多線程的運作時庫)

單線程情況下:

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

///

多線程下vc2003,vc2005對虛函數表處理的BUG?

///

多線程下vc2003,vc2005對虛函數表處理的BUG?

/// to demonstrate the fucking bug.

多線程下vc2003,vc2005對虛函數表處理的BUG?

///

多線程下vc2003,vc2005對虛函數表處理的BUG?

/// OK, even it links the multi-thread crt.

多線程下vc2003,vc2005對虛函數表處理的BUG?

#include  < iostream >

多線程下vc2003,vc2005對虛函數表處理的BUG?

#include  " kl_thread.h "

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

class  Window

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

{

多線程下vc2003,vc2005對虛函數表處理的BUG?

public:

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

    /// 

多線程下vc2003,vc2005對虛函數表處理的BUG?

    virtual ~Window()

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

{

多線程下vc2003,vc2005對虛函數表處理的BUG?

        std::cout << "~Window" << std::endl;

多線程下vc2003,vc2005對虛函數表處理的BUG?

    }

多線程下vc2003,vc2005對虛函數表處理的BUG?

} ;

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

class  Listener

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

{

多線程下vc2003,vc2005對虛函數表處理的BUG?

public:

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

    /// as most listener class, it only put some interface here

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

    virtual void show() 

多線程下vc2003,vc2005對虛函數表處理的BUG?

{}

多線程下vc2003,vc2005對虛函數表處理的BUG?

} ;

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

/// 

多線程下vc2003,vc2005對虛函數表處理的BUG?

Listener  * gListener;

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

class  App :  public  Window,  public  Listener

多線程下vc2003,vc2005對虛函數表處理的BUG?

// class App : public Listener, public Base

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

{

多線程下vc2003,vc2005對虛函數表處理的BUG?

public:

多線程下vc2003,vc2005對虛函數表處理的BUG?

    void init()

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

{

多線程下vc2003,vc2005對虛函數表處理的BUG?

        gListener = this;

多線程下vc2003,vc2005對虛函數表處理的BUG?

    }

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

    /// implement the interface

多線程下vc2003,vc2005對虛函數表處理的BUG?

    void show()

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

{

多線程下vc2003,vc2005對虛函數表處理的BUG?

        std::cout << "App::show" << std::endl;

多線程下vc2003,vc2005對虛函數表處理的BUG?

    }

多線程下vc2003,vc2005對虛函數表處理的BUG?

} ;

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

App gApp;

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

int  main()

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

{

多線程下vc2003,vc2005對虛函數表處理的BUG?

    gApp.init();

多線程下vc2003,vc2005對虛函數表處理的BUG?
多線程下vc2003,vc2005對虛函數表處理的BUG?

    gListener->show();

多線程下vc2003,vc2005對虛函數表處理的BUG?

    return 0;

多線程下vc2003,vc2005對虛函數表處理的BUG?

}

多線程下vc2003,vc2005對虛函數表處理的BUG?

無論Listener, Window的順序如何,一切都很正常。這起碼說明了,在語言層次,我的做法是正确的。

而這個時候即使連結了多線程的運作時庫,結果也是正确的。

那麼錯誤可以歸結于多線程,可能是在多線程下編譯器對虛函數表初始化不正确所緻。這是否真的是

VC2003、VC2005的BUG?

posted on 2008-04-24 14:40 Kevin Lynx 閱讀(866) 評論(10)  編輯 收藏 引用 所屬分類: C++

多線程下vc2003,vc2005對虛函數表處理的BUG?

<script type="text/javascript"> // </script>

評論

還是寫出來看的清楚:static unsigned int __stdcall ThreadFunc( void *p )

{

Listener *listener = (Listener*) p;

應該是

Listener *listener = (App*) p;

回複  更多評論   

# re: 多線程下vc2003,vc2005對虛函數表處理的BUG? 2008-04-24 17:34 giscn

或者這樣

_game_thread = (HANDLE)_beginthreadex( NULL, 0, ThreadFunc, (Listener*)this, 0, NULL );  回複  更多評論   

# re: 多線程下vc2003,vc2005對虛函數表處理的BUG? 2008-04-24 17:40 giscn

錯誤由void* 指針轉換引起,與多線程無關,C++ 的指針轉換不同于C, 如果是多集繼承,參數同樣是 this, 其實際值不一定相同,取決于參數類型  回複  更多評論   

# re: 多線程下vc2003,vc2005對虛函數表處理的BUG? 2008-04-24 17:40 eXile

樓上的正解。在使用多重繼承時要注意對象的布局。  回複  更多評論   

# re: 多線程下vc2003,vc2005對虛函數表處理的BUG? 2008-04-24 18:09 亨德列克

Listener *listener = (Listener*) p; 是這一行錯了,這個錯誤應該很多人都會犯……  回複  更多評論   

# re: 多線程下vc2003,vc2005對虛函數表處理的BUG? 2008-04-24 19:18 Kevin Lynx

@亨德列克

不是那一行錯了,App和Game兩個類分屬不同子產品,為了不讓兩個子產品耦合,這裡使用Listener *listener = (Listener*) p,而不是(App*)p。

giscn和eXile (他删除了他的第二條評論:) )的方法是正确的。可以被采用,再次表示感謝。

這讓我意識到,void*在C++裡缺乏安全性。  回複  更多評論   

# re: 多線程下vc2003,vc2005對虛函數表處理的BUG? 2008-04-24 22:49 飯中淹

這不是多線程的問題

當你先繼承window後繼承Listener的時候,App的記憶體結構如下:

class App

vt of Window

data of Window

vt of Listener

data of Listener

data of App

_beginthreadex的參數是void*,你把this傳遞進去,相當于傳遞CApp* this。其實隐含的就是Window*this,那麼裡面調用Listener->Show,自然就會去Window的vt裡面查找對應索引的函數,就會調用錯函數。

而第二個,因為你顯式的=this,是以,編譯器會進行轉換,進而把正确的Listener位址指派給那個全局指針,這時,無論繼承順序如何,都是正确的結果。

這其實是因為對象指針轉換不準确導緻的,不是vc的bug,也不是多線程的問題。

  回複  更多評論   

# re: 多線程下vc2003,vc2005對虛函數表處理的BUG? 2008-04-25 08:32 FongLuo

收藏,收藏,^_^  回複  更多評論   

# re: 多線程下vc2003,vc2005對虛函數表處理的BUG? 2008-04-25 17:03 #Ant

多繼承下還有這樣的問題,學習了。。。  回複  更多評論   

# re: 多線程下vc2003,vc2005對虛函數表處理的BUG?[未登入] 2008-04-26 00:49 楊粼波

指針偏移,多線程會發生這樣滴問題。。。。  

#  re: 多線程下vc2003,vc2005對虛函數表處理的BUG? 2008-04-24 16:09 Fox