2012-04-03 26 views
1

所以,我讀了很多關於爲什麼此實現不是線程安全的。但我沒有找到答案如何使線程安全和快速?使線程安全的變體是添加互斥體(或者在某些情況下,只有關鍵部分就足夠了),但它會使這種方法變得更慢。那麼是否有一種變體可以使代碼線程安全快速,或者至少現在像在其中添加互斥體一樣慢?讓邁爾斯辛格爾頓線程安全,快速與懶惰的評價

static Singleton& getInstance() 
{ 
    static Singleton singleton; 
    return singleton; 
} 

PS:是的,我也看了很多關於線程安全Singletom實現,當我們使用辛格爾頓指針作爲類的成員,該問題是關於這個特定的實現辛格爾頓,沒有指針和新和使用懶惰的評價。

+2

以下是一個提示:您遇到的實現問題可能表明您嘗試執行的操作是錯誤的。 – Puppy 2012-04-03 13:47:34

+0

爲什麼不使用全局變量而不是單例? – RedX 2012-04-03 13:51:49

+0

@DeadMG我認爲最有可能我所問的是不可能的,但我不確定,所以我問=) – Alecs 2012-04-03 14:05:05

回答

0

所以似乎回答我的問題在他的評論中表達得最淋漓盡致弗雷德·拉森:

「快速,線程安全的,懶惰 - 挑選任何兩個。」

0

好的,所以你不能沒有互斥體就可以完成它,但是你可以快速地完成這個互斥體。

首先聲明一個類來保存互斥體,和就緒標誌(以下InitMutex)。當調用GrabMutex()並且readyfalse時,實際的互斥鎖被抓取。當調用ReleaseMutex()時,它根據從GrabMutex()發送的標誌執行正確的操作。第一次通過後,隨着靜態對象被初始化,準備就緒,因此不需要抓取互斥鎖。

現在,聲明一個類,在構造函數中調用GrabMutex(),在析構函數中調用ReleaseMutex(flag),並在本地保存標誌(InitMutexHolder)。

現在,在您的常規getSingleton函數中實例化該類。這將確保單例初始化在第一次通過時被互斥,並且如果多個線程競爭他們將排隊在互斥體上。但是,一旦單身人士初始化,ready成爲現實,並且訪問將會很快。在執行return theSingleton之後,析構函數被神奇地調用,釋放互斥量(或者,如果互斥量未被採用,則不執行任何操作)。

但是,theSingleton()函數中的主體代碼沒有變化,我們只在每個單例中添加了一個控制對象,並在調用中添加了一個管理線程安全的堆棧對象。

關於寫屏障的注意事項:因爲代碼是安全的,只要ready爲假,並且ready在對象初始化之後才能變爲真,代碼總體安全,假設寫操作立即可見。但是,ready=true可能會延遲顯示,因爲在設置準備就緒後沒有寫屏障。然而,在此期間安全性仍然保持不變,因爲ready=false是保守的,安全的情況。

class InitMutex 
{ 
public: 
    InitMutex() : ready(false) { } 

    bool GrabMutex() 
    { 
     if (!ready) 
     { 
     mutex.Grab(); 
     return true; 
     } 
     else 
     { 
     return false; 
     } 
    } 

    void ReleaseMutex(bool flagFromGrabMutex) 
    { 
     if (flagFromGrabMutex) 
     { 
      mutex.Release(); 
      ready = true; 
     } 
    } 

    Mutex mutex; 
    bool ready; 
}; 

class InitMutexHolder 
{ 
public: 
    InitMutexHolder(InitMutex & m) 
    : initMutex(m) 
    { 
     inMutex = initMutex.GrabMutex(); 
    } 
    ~InitMutexHolder() 
    { 
     initMutex.ReleaseMutex(inMutex); 
    } 

private: 
    bool inMutex; 
    InitMutex & initMutex; 
}; 

static InitMutex singletonMutex; 
static Singleton & getSingleton() 
{ 
    InitMutexHolder mutexHolder(singletonMutex); 
    { 
     static Singleton theSingleton; 
     return theSingleton; 
    } 
} 
+0

我想我忘了添加主題詞「懶惰評估」,對不起。 – Alecs 2012-04-03 13:46:50

+0

我清楚地明白,Singleton只初始化一次,並且在main之前初始化它將是線程安全的,但它不會是懶惰的評估。 – Alecs 2012-04-03 13:53:37

+0

我知道你現在寫的變體。它在我的,我想諮詢一下實現無新指針PS問題陳述。我猜先前問我的問題,答案是「不,這是不可能的」,你的文章似乎回答相同) – Alecs 2012-04-03 14:03:19

2

對於某些編譯器,您可能已擁有線程安全保證。如果你不關心代碼可移植性,並且它適合你,那麼請對此感到滿意。

如果您有可用升壓線程可以使用boost::call_once初始化。這是線程安全的,只在第一次初始化時花費。

你當然也可以通過初始化它來創建一個完全線程安全的「Meyers」單例創建,即在創建訪問它的線程之前,首次訪問它。如果你有很多已經實現的單身人士考慮這樣做。

所有這些,甚至boost::call_once只適用於創建對象。但是,它的訪問權限可能需要單獨的同步技術,如果由多個線程訪問。 (附帶說明Meyers Effective C++的第47項,其中提到這個單例表明後面的標準修訂使得它是線程安全的,後來的編譯器也符合它,但它警告你並不​​是所有的編譯器都符合標準)。

+0

+1提這已經是線程安全的(在C++ 11) – Fiktik 2013-01-07 13:59:17

+0

是的,邁爾斯單的一大缺點是這些對象的不確定性破壞秩序,如果有任何依賴於其他人的情況,則不能保證哪一個會先發生,這可能會導致問題。 – CashCow 2013-01-09 10:54:28