1. 程式人生 > >再談單例模式

再談單例模式

之前寫過一篇部落格 《C++單例模式的模板基類》 http://blog.csdn.net/kentzhang_/article/details/48206821 

但是,最近才發現實際 上 static  T* Instance() 的實現是有一點bug 的,下面分析。


 static T *Instance()  
        {  
            if(NULL != m_Instance)  
            {  
                return m_Instance;  
            }  
  
            m_Mutex.Lock();  
            if(NULL != m_Instance)  
            {  
                m_Mutex.UnLock();  
                return m_Instance;  
            }  
  
            m_Instance = new(std::nothrow) T();  
            m_Mutex.UnLock();  
            return m_Instance;  
        }  

這種實現在多執行緒環境下,可能會有bug ,原因就在 m_Instance = new (std::nothrow) T() 這行程式碼。

這句程式碼實際上分三步執行:

1、分配記憶體

2、呼叫建構函式,初始化記憶體空間

3、將記憶體地址賦值給單例指標

程式碼可轉化為如下形式:

T*  p  =  newMemory(sizeof(T))

p->BaseSingleton::BaseSingleton()

m_Instance = p

編譯器出於優化的目的,可能會將第一步和第三步合併,變成如下:

m_Instance  =  newMemory(sizeof(T))

m_Instance->BaseSingleton::BaseSingleton()

那麼在執完第一行程式碼時,有可能會切換到其他執行緒,其他執行緒如果呼叫 Instance()函式,由於m_Insatnce已經賦值,將返回這個指標,但是

指標指向的記憶體並未初始化,所以會造成不可預知的錯誤。


在Boost庫中,有一種很好的單例實現模式,我做了一點修改,就是引用改成指標。程式碼如下:

#ifndef BASESINGLETON_H
#define BASESINGLETON_H

/**
  單例模式採用boost原始碼
  由於boost單例返回的是引用,下面改成指標
  */
template<class T>
class BaseSingleton
{
public:
    BaseSingleton(){}
private:
    struct object_creator
    {
        // This constructor does nothing more than ensure that instance()
        //  is called before main() begins, thus creating the static
        //  T object before multithreading race issues can come up.
        object_creator() { BaseSingleton<T>::Instance(); }
        inline void do_nothing() const { }
    };
    static object_creator create_object;

    //BaseSingleton();

public:
    typedef T object_type;

    // If, at any point (in user code), BaseSingleton<T>::instance()
    //  is called, then the following function is instantiated.
    static object_type* Instance()
    {
        // This is the object that we return a reference to.
        // It is guaranteed to be created before main() begins because of
        //  the next line.
        static object_type obj;

        // The following line does nothing else than force the instantiation
        //  of BaseSingleton<T>::create_object, whose constructor is
        //  called before main() begins.
        create_object.do_nothing();

        return &obj;
    }
};
template <class T>
typename BaseSingleton<T>::object_creator
BaseSingleton<T>::create_object;

#endif // BASESINGLETON_H

我之前實現的單例模式中,必須是第一次手動呼叫Instance()才生成單例物件,這樣就引入了多執行緒的問題。

而Boost實現方式,在單例類中定義一個內嵌的靜態的結構體,這個結構體生成時呼叫自己的建構函式,建構函式中執行Instance()函式,由於單例類內的靜態的結構體生成時,是在main()執行之前,所以巧妙地繞開了多執行緒的問題。