【模式】單件模式及其多線程版本

很多情況下需要將我們編寫的程序中的類在一定範圍內只保留一個實例,例如出於性能考慮,我們不希望反覆實例化這個對象,用完了後再將它銷燬,然後又用到這個類型的某些服務,再實例化,用完了再銷燬,如此反覆,不如做個全局的,實例化後,一直用到本程序功能結束,但是全局的又有一些問題。
        經典的Singleton模式可以爲我們提供一個簡單的解決方案,他提供了一個全局訪問點,用來訪問這個唯一實例,簡單思想是:當外界想要持有這個類型的實例時,這個類型會自己檢查這個實例是否已經被創建,如果沒有則創建一個並返回這個實例的指針/引用,有了,則簡單的返回這個對象的指針/引用即可。而且爲了確保該類型的實例的唯一性,將構造函數protected或private。

        單件模式的示例代碼如下:
class Singleton
...{
public:
         
static Singleton* GetInstance();
protected:
          Singleton();
private:
         
static Singleton*  m_pInstance;
}
;

Singleton
* Singleton::m_pInstance = 0;

Singleton
* Singleton::GetInstance()
...{
           
if(m_pInstance == 0)
           
...{
                      m_pInstance
= new Singleton;
            }

           
return m_pInstance;
}
        使用時,僅需要通過GetInstance成員函數訪問這個單件,然後訪問這個類型的其他服務,這樣做最直接的好處就是避免了全局變量帶來的污染命名空間的問題,同時也避免了全局對象的過早實例化的問題(因爲全局對象會在main執行前處理,所以不論我們的程序需不需要訪問這個類型,他都被實例化了,用singleton模式可以將他的實例化推遲到必需時)。

        單件模式還可以與工廠模式連用,即可以在GetInstance中判斷一下需要的到底是那一種單件,然後用一個子類的對象返回給一個基類的指針就可以了。不過這些場模式的實例必須遵循單件模式。
         當然原型模式也可以連用,我之前有一篇blog介紹了我學mfc的時候做的一個簡單的畫圖程序,當時用到了原型模式和場模式,裏面的畫筆和畫刷其實可以做成單件模式,這樣就不必在用戶每次切換畫筆或畫刷的時候new一個pen或者brush了。

         上面說的情況沒有考慮到線程問題,即如果存在多個線程要訪問我們這個類的那個實例,就會有這樣、那樣的問題:
         1.如果第一個線程訪問這個類時,m_pInstance == 0爲true,那麼他要new一個Singleton的對象,恰巧第二個線程也訪問這個類,這是還沒有new 這個類的對象,而且m_pInstance也是空,那麼他也會new一個Singleton的對象,這就有問題,明明創建了兩個對象麼!析構時怎麼辦?只能內存泄露吧?
         2.由於對象是唯一的,如果這個Singleton有一個方法是取得一組值,另一個方法是遍歷這組值,不加鎖的情況下,可能會在第一個線程遍歷到那組值的中間的時候,另一個線程開始遍歷,在這樣比較背的情況下,你往下遍歷一個,我往下遍歷一個,就亂套了~
         ......

        解決辦法就是在m_pInstance上加鎖,那麼類似第二種情況的問題就可以解決,但是第一種情況呢?我們需要做的可能就是對“檢查Singleton對象已經被創建”進行同步。這麼做挺簡單,不過會造成一個性能上的瓶頸,所有線程都必需等待檢查對象是否存在。也許這個對象已經早在800年前就創建了,不過還得鎖上-檢查-解鎖,浪費資源。另一種做法就是被稱爲雙重檢查-鎖定的模式Doublechecked Locking!其意圖就是將上面的那些已經存在對象的情況的鎖上-檢查-解鎖的步驟省略掉。因此不會造成系統的瓶頸,問題只出現在第一次創建對象的時候,那麼就把他提取出來好了。示例代碼如下:
Singleton* Singleton::GetInstance()
...{
           
if(m_pInstance == 0)
           
...{
                     
//在這裏同步,加鎖
                      if(m_pInstance == 0)
                     
...{
                             m_pInstance
= new Singleton;
                      }
                    
            }

           
return m_pInstance;
}
        外層的if判斷檢查了已經存在實例的情況,內層的if判斷則是上面所描述的第一種情況的檢查。不過可惜,目前C++標準還沒有引入線程方面的庫,不知道boost中有沒有。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章