【設計模式】09 Singleton單例模式

Singleton單例模式

1 動機

(1)在軟件系統中,經常有這樣一些特殊的類,必須保證他們在系統中只存在一個實例,才能確保他們的邏輯正確性,以及良好的效率。
(2)如何繞過常規的構造器,提供一種機制來保證一個類只有一個實例?
注:工廠模式之類的是繞過new,來解決緊耦合問題;單例模式是繞過常規的構造器,解決性能問題
(3)這應該是類設計者的責任,而不是使用者的責任。

2 模式定義

保證一個類僅有一個實例,並提供一個該實例的全局訪問點。

3 結構

在這裏插入圖片描述

4 僞代碼

class Singleton{
private://不寫這個構造和拷貝構造不行,編譯器會默認有缺省的共有的構造和拷貝構造
//讓外界不能用就設置私有
    Singleton();
    Singleton(const Singleton& other);
	Singleton&operator=(const Singleton&);
public:
    static Singleton* getInstance();//靜態函數
    static Singleton* m_instance;//靜態變量
};

Singleton* Singleton::m_instance=nullptr;//設置成nullptr確認這是一個堆對象

//線程非安全版本(多線程不安全)
Singleton* Singleton::getInstance() {
    if (m_instance == nullptr) {//多線程可能同時進來,那麼就會創建多個m_instance對象
        m_instance = new Singleton();
    }
    return m_instance;
}



//線程安全版本,但鎖的代價過高
Singleton* Singleton::getInstance() {
    Lock lock;
    if (m_instance == nullptr) {
        m_instance = new Singleton();
    }
    return m_instance;
}



//雙檢查鎖,但由於內存讀寫reorder(重排列)不安全
Singleton* Singleton::getInstance() {
    
    if(m_instance==nullptr){
        Lock lock;
        if (m_instance == nullptr) {
            m_instance = new Singleton();//常規默認:1分配內存;2調用構造器;3賦值給m_instance。(編譯器可能會出現重排列)
			
        }
    }
    return m_instance;
}



//加入volatile,那麼編譯器就會根據常規步驟進行,而不會對代碼進行優化
//C++ 11版本之後的跨平臺實現 (volatile)
std::atomic<Singleton*> Singleton::m_instance;
std::mutex Singleton::m_mutex;

Singleton* Singleton::getInstance() {
    Singleton* tmp = m_instance.load(std::memory_order_relaxed);//屏蔽編譯器的reorder
    std::atomic_thread_fence(std::memory_order_acquire);//獲取內存fence(柵欄屏蔽)//這兩步屏蔽編譯器的reorder
    if (tmp == nullptr) {
        std::lock_guard<std::mutex> lock(m_mutex);
        tmp = m_instance.load(std::memory_order_relaxed);
        if (tmp == nullptr) {
            tmp = new Singleton;
            std::atomic_thread_fence(std::memory_order_release);//釋放內存fence
            m_instance.store(tmp, std::memory_order_relaxed);
        }
    }
    return tmp;
}

5 要點總結

(1)Singleton模式中的實例構造器可以設置爲protected以允許子類派生。
(2)Singleton模式一般不要支持拷貝構造函數的Clone接口,因爲這有可能導致多個對象實例,與Singleton模式的初衷違背。
(3)如何實現多線程環境下安全的Singleton?注意對雙檢查鎖的正確實現。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章