2010-08-09 48 views
3

在最近的一個項目中,我不得不創建一個Singleton類,經過在Google上大量的研究,我想出了這個模板類的定義。這個想法是從這個模板類派生,並使派生類的構造函數protected/private。它似乎工作得很好,但我只用它在一個項目中的單個類,所以我希望你們中的一些人可以指出,如果我在實現中犯了錯誤。那就是:C++單例模板類

/** 
* @brief 
* Singleton design pattern implementation using a dynamically allocated singleton instance. 
* 
* The SingletonDynamic class is intended for use as a base for classes implementing the Singleton 
* design pattern and require lazy initialization of the singleton object. The default 
* implementation is not thread-safe, however, the derived classes can make it so by reinitializing 
* the function pointers SingletonDynamic<T>::pfnLockMutex, SingletonDynamic<T>::pfnUnlockMutex 
* and SingletonDynamic<T>::pfnMemoryBarrier. The member function pointers are initialized by 
* default to point to placeholder functions that do not perform any function. The derived class 
* must provide alternate implementations for SingletonDynamic<T>::lock_mutex(), 
* SingletonDynamic<T>::unlock_mutex() and SingletonDynamic<T>::memory_barrier() respectively 
* and reinitialize the respective function pointer members to these alternate implementations. 
* 
* @tparam T 
* The type name of the derived (singleton) class 
* 
* @note The derived class must have a no-throw default constructor and a no-throw destructor. 
* @note The derived class must list this class as a friend, since, by necessity, the derived class' 
*  constructors must be protected/private. 
*/ 
template< typename T > 
class SingletonDynamic 
{ 
public: 
    /** 
    * Factory function for vending mutable references to the sole instance of the singleton object. 
    * 
    * @return A mutable reference to the one and only instance of the singleton object. 
    */ 
    static T &instance() 
    { 
    return *SingletonDynamic<T>::get_instance(); 
    } 


    /** 
    * Factory function for vending constant references to the sole instance of the singleton object. 
    * 
    * @return A constant reference to the one and only instance of the singleton object. 
    */ 
    static const T &const_instance() 
    { 
    return *SingletonDynamic<T>::get_instance(); 
    } 

protected: 
    /** Default constructor */ 
    SingletonDynamic() {} 

    /** Destructor */ 
    virtual ~SingletonDynamic() 
    { 
    delete SingletonDynamic<T>::pInstance_; 
    } 

    /** Defines an alias for a function pointer type for executing functions related to thread-safety */ 
    typedef void(*coherence_callback_type)(); 

    /** 
    * Pointer to a function that will lock a mutex denying access to threads other that the current 
    * 
    * @note The function must have the signature void foo() 
    * @note The derived class must never set this variable to NULL, doing so will cause a crash. The 
    *  default value must be left unchanged if this functionality is not desired. 
    */ 
    static coherence_callback_type pfnLockMutex; 

    /** 
    * Pointer to a function that will unlock a mutex allowing access to other threads 
    * 
    * @note The function must have the signature void foo() 
    * @note The derived class must never set this variable to NULL, doing so will cause a crash. The 
    *  default value must be left unchanged if this functionality is not desired. 
    */ 
    static coherence_callback_type pfnUnlockMutex; 

    /** 
    * Pointer to a function that executes a memory barrier instruction that prevents the compiler 
    * from reordering reads and writes across this boundary. 
    * 
    * @note The function must have the signature void foo() 
    * @note The derived class must never set this variable to NULL, doing so will cause a crash. The 
    *  default value must be left unchanged if this functionality is not desired. 
    */ 
    static coherence_callback_type pfnMemoryBarrier; 

private: 
    /** The sole instance of the singleton object */ 
    static T *pInstance_; 

    /** Flag indicating whether the singleton object has been created */ 
    static volatile bool flag_; 

    /** Private copy constructor to prevent copy construction */ 
    SingletonDynamic(SingletonDynamic const &); 

    /** Private operator to prevent assignment */ 
    SingletonDynamic &operator=(SingletonDynamic const &); 


    /** 
    * Fetches a pointer to the singleton object, after creating it if necessary 
    * 
    * @return A pointer to the one and only instance of the singleton object. 
    */ 
    static T *get_instance() 
    { 
    if(SingletonDynamic<T>::flag_ == false) { 
     /* acquire lock */ 
     (*SingletonDynamic<T>::pfnLockMutex)(); 

     if(SingletonDynamic<T>::pInstance_ == NULL) { 
     pInstance_ = new T(); 
     } 

     /* release lock */ 
     (*SingletonDynamic<T>::pfnUnlockMutex)(); 

     /* enforce all prior I/O to be completed */ 
     (*SingletonDynamic<T>::pfnMemoryBarrier)(); 

     SingletonDynamic<T>::flag_ = true; 

     return SingletonDynamic<T>::pInstance_; 
    } else { 
     /* enforce all prior I/O to be completed */ 
     (*SingletonDynamic<T>::pfnMemoryBarrier)(); 

     return SingletonDynamic<T>::pInstance_; 
    } 
    } 


    /** 
    * Placeholder function for locking a mutex, thereby preventing access to other threads. This 
    * default implementation does not perform any function, the derived class must provide an 
    * implementation if this functionality is desired. 
    */ 
    inline static void lock_mutex() 
    { 
    /* default implementation does nothing */ 
    return; 
    } 


    /** 
    * Placeholder function for unlocking a mutex, thereby allowing access to other threads. This 
    * default implementation does not perform any function, the derived class must provide an 
    * implementation if this functionality is desired. 
    */ 
    inline static void unlock_mutex() 
    { 
    /* default implementation does nothing */ 
    return; 
    } 


    /** 
    * Placeholder function for executing a memory barrier instruction, thereby preventing the 
    * compiler from reordering read and writes across this boundary. This default implementation does 
    * not perform any function, the derived class must provide an implementation if this 
    * functionality is desired. 
    */ 
    inline static void memory_barrier() 
    { 
    /* default implementation does nothing */ 
    return; 
    } 
}; 

/* Initialize the singleton instance pointer */ 
template< typename T > 
T *SingletonDynamic<T>::pInstance_  = NULL; 

/* Initialize the singleton flag */ 
template< typename T > 
volatile bool SingletonDynamic<T>::flag_ = false; 

/* Initialize the function pointer that locks the mutex */ 
template< typename T > 
typename SingletonDynamic<T>::coherence_callback_type SingletonDynamic<T>::pfnLockMutex 
                   = &SingletonDynamic<T>::lock_mutex; 

/* Initialize the function pointer that unlocks the mutex */ 
template< typename T > 
typename SingletonDynamic<T>::coherence_callback_type SingletonDynamic<T>::pfnUnlockMutex 
                   = &SingletonDynamic<T>::unlock_mutex; 

/* Initialize the function pointer that executes the memory barrier instruction */ 
template< typename T > 
typename SingletonDynamic<T>::coherence_callback_type SingletonDynamic<T>::pfnMemoryBarrier 
                   = &SingletonDynamic<T>::memory_barrier; 

我特別擔心在頭文件是否是靜態成員初始化將導致多個定義錯誤時,被包含在多個是從SingleDynamic派生的類的頭文件文件。我已經試過了,它似乎工作,但我不明白爲什麼它的工作:)。

在此先感謝, Ashish。

編輯:修改實施使用在接受的解決方案中建議的基於策略的設計。

/** 
* This is the default ConcurrencyPolicy implementation for the SingletonDynamic class. This 
* implementation does not provide thread-safety and is merely a placeholder. Classes deriving from 
* SingletonDynamic must provide alternate ConcurrencyPolicy implementations if thread-safety is 
* desired. 
*/ 
struct DefaultSingletonConcurrencyPolicy 
{ 
    /** 
    * Placeholder function for locking a mutex, thereby preventing access to other threads. This 
    * default implementation does not perform any function, the derived class must provide an 
    * alternate implementation if this functionality is desired. 
    */ 
    static void lock_mutex() 
    { 
    /* default implementation does nothing */ 
    return; 
    } 

    /** 
    * Placeholder function for unlocking a mutex, thereby allowing access to other threads. This 
    * default implementation does not perform any function, the derived class must provide an 
    * alternate implementation if this functionality is desired. 
    */ 
    static void unlock_mutex() 
    { 
    /* default implementation does nothing */ 
    return; 
    } 

    /** 
    * Placeholder function for executing a memory barrier instruction, thereby preventing the 
    * compiler from reordering read and writes across this boundary. This default implementation does 
    * not perform any function, the derived class must provide an alternate implementation if this 
    * functionality is desired. 
    */ 
    static void memory_barrier() 
    { 
    /* default implementation does nothing */ 
    return; 
    } 
}; 


/** 
* @brief 
* Singleton design pattern implementation using a dynamically allocated singleton instance. 
* 
* The SingletonDynamic class is intended for use as a base for classes implementing the Singleton 
* design pattern and that dynamic allocation of the singleton object. The default implementation 
* is not thread-safe; however, the class uses a policy-based design pattern that allows the derived 
* classes to achieve threaad-safety by providing an alternate implementation of the 
* ConcurrencyPolicy. 
* 
* @tparam T 
* The type name of the derived (singleton) class 
* @tparam ConcurrencyPolicy 
* The policy implementation for providing thread-safety 
* 
* @note The derived class must have a no-throw default constructor and a no-throw destructor. 
* @note The derived class must list this class as a friend, since, by necessity, the derived class' 
*  constructors must be protected/private. 
*/ 
template< typename T, typename ConcurrencyPolicy = DefaultSingletonConcurrencyPolicy > 
class SingletonDynamic : public ConcurrencyPolicy 
{ 
public: 
    /** 
    * Factory function for vending mutable references to the sole instance of the singleton object. 
    * 
    * @return A mutable reference to the one and only instance of the singleton object. 
    */ 
    static T &instance() 
    { 
    return *SingletonDynamic< T, ConcurrencyPolicy >::get_instance(); 
    } 


    /** 
    * Factory function for vending constant references to the sole instance of the singleton object. 
    * 
    * @return A constant reference to the one and only instance of the singleton object. 
    */ 
    static const T &const_instance() 
    { 
    return *SingletonDynamic< T, ConcurrencyPolicy >::get_instance(); 
    } 

protected: 
    /** Default constructor */ 
    SingletonDynamic() {} 

    /** Destructor */ 
    virtual ~SingletonDynamic() 
    { 
    delete SingletonDynamic< T, ConcurrencyPolicy >::pInstance_; 
    } 

private: 
    /** The sole instance of the singleton object */ 
    static T *pInstance_; 

    /** Flag indicating whether the singleton object has been created */ 
    static volatile bool flag_; 

    /** Private copy constructor to prevent copy construction */ 
    SingletonDynamic(SingletonDynamic const &); 

    /** Private operator to prevent assignment */ 
    SingletonDynamic &operator=(SingletonDynamic const &); 


    /** 
    * Fetches a pointer to the singleton object, after creating it if necessary 
    * 
    * @return A pointer to the one and only instance of the singleton object. 
    */ 
    static T *get_instance() 
    { 
    if(SingletonDynamic< T, ConcurrencyPolicy >::flag_ == false) { 
     /* acquire lock */ 
     ConcurrencyPolicy::lock_mutex(); 

     /* create the singleton object if this is the first time */ 
     if(SingletonDynamic< T, ConcurrencyPolicy >::pInstance_ == NULL) { 
     pInstance_ = new T(); 
     } 

     /* release lock */ 
     ConcurrencyPolicy::unlock_mutex(); 

     /* enforce all prior I/O to be completed */ 
     ConcurrencyPolicy::memory_barrier(); 

     /* set flag to indicate singleton has been created */ 
     SingletonDynamic< T, ConcurrencyPolicy >::flag_ = true; 

     return SingletonDynamic< T, ConcurrencyPolicy >::pInstance_; 
    } else { 
     /* enforce all prior I/O to be completed */ 
     ConcurrencyPolicy::memory_barrier(); 

     return SingletonDynamic< T, ConcurrencyPolicy >::pInstance_; 
    } 
    } 
}; 

/* Initialize the singleton instance pointer */ 
template< typename T, typename ConcurrencyPolicy > 
T *SingletonDynamic< T , ConcurrencyPolicy >::pInstance_  = NULL; 

/* Initialize the singleton flag */ 
template< typename T, typename ConcurrencyPolicy > 
volatile bool SingletonDynamic< T , ConcurrencyPolicy >::flag_ = false; 
+3

[你做**不需要**單身人士。](http://jalf.dk/blog/2010/03/singletons-solving-problems-you-didnt-know-you-never-had-since -1995 /)不要使用單身。你想要一個全球的,所以使用全球。 – GManNickG 2010-08-09 22:06:09

+1

我同意GMan不要使用singeltons。我不同意它們與全局變量相同(延遲初始化)。我討厭使用指針作爲表示,因爲它們不會自動刪除(在get_instance()中使用靜態函數變量(保持鎖定),這樣singleton將被正確刪除)。 PS。您需要在鎖內移動'flag_ = true;',否則您可能會創建多個線程來創建實例。 – 2010-08-09 23:33:20

+1

@Martin:你可以偷獨立創作的解決方案來創建一個不錯的全局實用程序庫。它主要是混淆和不必要的限制性實例廢話。我正在考慮向Boost提交一個全球圖書館,因爲它缺少一個。 – GManNickG 2010-08-10 00:25:20

回答

1

這裏併發相關代碼的正確性很難評估。在我看來,這個實現有點太聰明瞭。

OTOH,所有與併發相關的代碼基本上都有一些存根,它們什麼都不做。如果這在非線程環境中使用,我認爲它應該沒問題。

但是,我也認爲你的擔心是有根據的。這些靜態成員的外部定義看起來像他們會違反one definition rule。個人而言,我認爲這個模板應該被重寫,將併發的東西作爲模板本身的策略參數,並且要求派生類在合適的.cpp文件中聲明它們自己的版本pInstance

其他人建議依靠編譯器特定的行爲來進行靜態局部變量的初始化。我不認爲這是一個可怕的建議,但是當你不能依靠編譯器來做正確的事情時,有一個選擇可能會很好。

+0

我喜歡基於策略的設計理念;那麼我可以使默認策略不做任何事情,並且派生類可以提供自己的實現來處理併發。我今晚會嘗試一下。謝謝! – Praetorian 2010-08-09 22:45:08

1
+0

該鏈接末尾的建議依賴編譯器特定的行爲來處理併發問題。 – Omnifarious 2010-08-09 22:15:16

+0

@Omnifarious,上面的鏈接只是使用一個靜態單例對象。爲什麼編譯器是特定的?對於所有編譯器來說,所有靜態對象都是在'main()'執行之前構造的,是不是真的?因此,只要所有調用線程在'main()'開始執行後執行,那麼該實現應該是線程安全的。 – Praetorian 2010-08-09 22:49:26

+0

@Praetorian:只有第一次使用時纔會創建靜態函數對象(允許惰性評估)。 Omnifarious所指的也是這種靜態的創建不是線程安全的,除非你使用的gcc已經以特定的方式實現了編譯器來確保它是線程安全的。 – 2010-08-09 23:29:37

3

模板類的靜態成員必須在頭文件中初始化,最近的C++編譯器及其連接器必須正確處理。

但你是對的,一些非常老的編譯器有這個問題。

在這些情況下,它是一種解決方法,可以在任意編譯單元中爲每個單獨模板所用的類型準確初始化一次靜態成員。

gcc文檔具有相同的細節:http://gcc.gnu.org/onlinedocs/gcc/Template-Instantiation.html

我記得一個嵌入式項目(不久前),其中一箇舊的編譯器仍在使用中,並且一個靜默地創建了模板靜態成員的多個實例。 很明顯,當它來存儲一個單身人士時,它是一個非常糟糕的想法....

更糟的是,庫中使用的唯一Singleton(第三方框架)是一些通常以相同方式初始化的Configuration對象,因此只有在運行時更改配置時纔會發生該錯誤。 我們花了幾天的時間來追蹤錯誤,直到我們終於在反彙編中看到「不同內存區域訪問同一個成員」。

1

目前不可能在C++的多線程環境中懶洋洋地創建一個Singleton。

許多大師(其中Herb Sutter)已經認識到標準的當前狀態並不能保證任何東西。有各種各樣的編譯器的竅門,並且boost爲此提供了once工具,但是它是編譯器特定指令的一個混雜集合...它不是標準的C++(這是線程不知道的)。

當前正在工作的唯一解決方案(按照標準)是在啓動多線程之前初始化Singleton,或者在保證只有一個線程將訪問它的一部分進程中。

C++ 0x使線程進入標準,並且特別保證即使在多線程的情況下(如果多個同時調用所有塊直到創建結束)本地靜態變量將僅被創建一次。因此,下面的方法:

static MyType& Instance() { static Instance MExemplar; return MExemplar; } 

的作品,在這種情況下,根本不需要單身模板類。