2013-03-08 29 views
2

我確實看到了(對我來說)奇怪的訪問衝突異常。我會盡量減少這個問題。我有一個A類和一個單身物體sing_。代碼看起來莫名其妙地像:在初始化程序列表中創建單例對象會導致訪問衝突(僅限發佈模式)

class A { 
    A(); 
    Sing& sing_; 
} 

A::A() : sing_(Sing::instance()){ 
    call a method that creates a local copy of Singleton Sing. 
    .... 
} 

類唱出單身繼承:

class Sing : public Singleton<Sing>{ 
    friend class Singleton<Sing>; 
    .... 
} 

辛格爾頓本身看起來像(這是在QuantLib庫的實現)

template <class T> 
class Singleton : private boost::noncopyable { 
    public: 
    static T& instance(); 
    protected: 
    Singleton() {} 
}; 


template <class T> 
T& Singleton<T>::instance() { 
    static std::map<Integer, boost::shared_ptr<T> > instances_; 
    #if defined(QL_ENABLE_SESSIONS) 
    Integer id = sessionId(); 
    #else 
    Integer id = 0; 
    #endif 
    boost::shared_ptr<T>& instance = instances_[id]; 
    if (!instance) 
     instance = boost::shared_ptr<T>(new T); 
    return *instance; 
} 

我的項目代碼嵌入在Qt Gui環境中。以調試模式啓動它不會引起麻煩。當我嘗試以發佈模式啓動時,情況發生可怕的變化。這是主要的方法:

int main(int argc, char *argv[]) 
{ 
    QApplication a(argc, argv); 
    GUI w; 
    w.show(); 
    w.setup(argc, argv); 

    return a.exec(); 
} 

最後類GUI看上去略像:

class GUI : public QMainWindow 
{ 
    Q_OBJECT 

public: 
    GUI(QWidget *parent = 0, Qt::WFlags flags = 0); 
    ~GUI(); 
private: 
    boost::shared_ptr<A> a_; 
}; 

當我在Release模式下啓動該代碼會發生以下情況:

  1. 的方法稱爲___ tmainCRTstartup()被調用。其中調用_WinMain方法。

  2. 該方法內部WinMain(甚至在主方法被調用並且創建GUI對象 之前)A的構造函數被調用。這意味着成員唱歌將被初始化。

  3. sing_在Sing :: instance()的調用過程中被初始化。一切看起來都很好。

  4. A的構造函數被執行。其中有一個地方參考Sington創建的單體對象。在該行

    boost::shared_ptr<T>& instance = instances_[id]; 
    

調用星::實例()導致訪問衝突當我看着instances_ [ID]在那個地方(調試在Release模式)地圖看起來相當銷燬。這意味着地圖中有一個元素。但關鍵不是0而是一個非常負的整數,而且這個值看起來很奇怪。

我絕對不知道這裏出了什麼問題。

在不斷變化的成員sing_是一個靜態成員解決了這個問題:

class A { 
    A(); 
    static Sing& sing_; 
} 

Sing& sing_ = Sing::instance(); 

A::A() { 
    call a method that creates a local copy of Singleton Sing. 
    .... 
} 

這當然不錯,但我真的不知道什麼是這兩種實現之間的「大」的區別。爲什麼第一種方式在訪問衝突中結束?任何暗示是讚賞。

+2

OP Y U SINGLETONS? – 2013-03-08 16:53:08

+2

單身並不是一個真正的邪惡,他們已經得到了他們的使用。 – 2013-03-08 16:57:56

+0

當然'sessionId'返回一些垃圾? PS:迄今爲止我所見過的單反反模式最糟糕的濫用。 – 2013-03-08 17:01:11

回答

0

我不知道這個問題的途徑,但我可以提供在得到有一些幫助:

所有,最常見的原因是,在調試運行的程序發佈在失敗的第一個(和副反之亦然):

  1. 在調試所有的內存通常是初始化(大多數調試我所知道的)爲0。所以,安全nullptr檢查,避免錯誤。在釋放內存不會被初始化,所以你得到垃圾/隨機值。

  2. 時間。代碼優化以及缺少調試檢查和設置(如將所有內容設置爲0)使代碼更快,因此線程之間的時間不同。

現在回到你的案例。你把變量視爲「被銷燬」的原因可能僅僅是因爲代碼優化。
通常,如果您啓用了代碼優化,編譯器可能會決定將一些變量放入硬件寄存器中,而不是按照預期放置在堆棧上。
這可能會導致調試器錯誤地解釋一些局部變量。 (最常見的是*這個指針存儲在一個寄存器中)。

現在,地圖上的運算符[]不應引發異常。如果地圖沒有該鍵的值,那麼它將被創建。

所以我能想到的唯一解釋是地圖本身已經損壞,所以當它試圖迭代地圖的節點時,它會崩潰。

這種類型的腐敗通常是由2個線程試圖同時改變地圖引起的。
在你的情況下,這是可能的,因爲在那個簡單的單例實現中沒有鎖保護。
另一個跡象表明,這可能是這樣的,當你把它變成靜態時,問題就解決了。使變量靜態導致對象的發起要早得多,這可能只是您需要解決線程競爭的時機。

+0

感謝您的答案!這很有趣。回答你的問題:1.我沒有使用多線程環境......至少我的部分代碼不是多線程的。我不知道Qt或qwt。是的,這是Sing的第一次創建。一切似乎都沒問題,但在Sing :: instance()的第二次調用中它崩潰了。你寫的關於操作符[]聽起來很合理。不知何故,地圖在第一次初始化後會「損壞」。在「靜態」情況下,Sing :: instance()被調用的情況也是如此。 – 2013-03-13 15:42:20

+0

關於優化,我還嘗試了一個沒有任何優化的版本。問題依然存在。關於「計時問題」的假設,我還會在通過代碼「調試」時看到這種行爲。至少在我看來,這是一個純粹的順序代碼流。但是,正如我所說的,A的構造函數在「WinMain」方法中被調用。甚至在物體在主要方法中「誕生」之前。我不知道WinMain會發生什麼。 – 2013-03-13 15:48:32

相關問題