閑聊c/c++ 8: 設(shè)計模式: 單例模式真的簡單嗎?(上)

設(shè)計模式簡介:

設(shè)計模式描述了對象如何進(jìn)行通信才能不牽涉相互的數(shù)據(jù)模型和方法。

保持這種獨立性一直是一個好的面向?qū)ο蟪绦蛟O(shè)計的目標(biāo)。 

Gang of Four的“Design Patterns: Elements of Resualbel Software”書將設(shè)計模式
歸納為三大類型,共23種。

創(chuàng)建型模式 :  通常和對象的創(chuàng)建有關(guān),涉及到對象實例化的方式。(共5種模式)

行為型模式: 通常和對象間通信有關(guān)。(共11種模式)

結(jié)構(gòu)型模式: 描述的是如何組合類和對象以獲得更大的結(jié)構(gòu)。(共7種模式)
                          
            類模式描述的是如何使用繼承提供更有用的程序接口。

            而對象模式描述的是如何通過使用對象組合或?qū)ο蟀谄渌麑ο罄铮?            將對象組合成更大的一個結(jié)構(gòu)。

單例模式的適用范圍:

1、單例模式可以保證:在一個應(yīng)用程序中,一個類有且只有一個實例,
 并提供一個訪問它的全局訪問點。

2、在程序設(shè)計過程中,有很多情況需要確保一個類只有一個實例。
 例如:
          windows系統(tǒng)中只能有一個窗口管理器

          某個程序中只能有一個日志輸出系統(tǒng)

          一個GUI類庫中,有且只有一個ImageManager

          還有其他無數(shù)種情況

教科書中的標(biāo)準(zhǔn)實現(xiàn):

教科書實現(xiàn).png

教課書中的標(biāo)準(zhǔn)實現(xiàn)的優(yōu)缺點:

1、優(yōu)點:該實現(xiàn)是一個"懶漢"單例模式,意味著只有在第一次調(diào)用GetInstance(),靜  態(tài)方法的時候才進(jìn)行內(nèi)存分配。如果整個程序不調(diào)用該靜態(tài)方法,則不會分配內(nèi)存。相對應(yīng)的是"餓漢"單例模式。

2、缺點:  1) "懶漢"模式雖然有優(yōu)點,但是每次調(diào)用GetInstance()靜態(tài)方法時,
              必須判斷NULL == m_instance,使程序相對開銷增大。

           2) 由于使用指針動態(tài)內(nèi)存分配,我們必須在程序結(jié)束時,
              手動的調(diào)用ReleaseInstance()靜態(tài)方法,進(jìn)行內(nèi)存的釋放。

           3) 教科書標(biāo)準(zhǔn)實現(xiàn)最大的缺點是線程不安全。
              根據(jù)該模式的定義,整個應(yīng)用程序中,不管是單線程,還是多線程,
              都只能有且只有該類的一個實例。而在多線程中會導(dǎo)致多個實例的產(chǎn)生,                 
              從而導(dǎo)致運行代碼不正確以及內(nèi)存的泄露。

教課書中的標(biāo)準(zhǔn)實現(xiàn)的線程不安全性演示:

線程不安全性演示代碼.png
VS2008線程不安全行演示效果.png
VS2015線程不安全演示效果.png
1、我們創(chuàng)建3個輔助線程,外加main主線程,一共有4個線程。

2、我們在每個輔助線程里面調(diào)用GetInstance()靜態(tài)方法,由于每個線程
  回調(diào)函數(shù)速度非??欤瑢?dǎo)致每個線程在判斷NULL==m_instance時,
  都返回true,從而導(dǎo)致每個線程回調(diào)函數(shù)都會創(chuàng)建一個CSingleton1對
  象并返回指向該對象的指針。

3、我們根本沒辦法進(jìn)行CSingleton1的內(nèi)存釋放,因為在多線程中,
   我們根本不知道是創(chuàng)建了1個、2個或3個CSingleton1的實例

Meyers Singleton Pattern實現(xiàn):

Meyers Singleton.png

Meyers Singleton Pattern的優(yōu)缺點 :

1、優(yōu)點:
           1)  該實現(xiàn)是一個"懶漢"單例模式,意味著只有在第一次調(diào)用GetInstance()時才會
                實例化。

           2)  不需要每次調(diào)用GetInstance()靜態(tài)方法時,必須判斷NULL==m_instance,效
                 率相對高一些。

           3)  使用對象而不是指針分配內(nèi)存,因此自動回調(diào)用析構(gòu)函數(shù),不會導(dǎo)致內(nèi)存泄露。

           4)  在多線程下的確能夠保證有且只有一個實例產(chǎn)生。
           

2、缺點:

          在某些編譯器中,在多線程情況下,并不是真正意義上的線程安全的實現(xiàn)

Meyers Singleton Pattern缺點演示:

我們修改一下前面線程函數(shù)

Instance2測試.png
VS2008Meyers單例線程不安全性演示.png

Meyers Singleton Pattern線程不安全性的原因:

    這是因為C++中構(gòu)造函數(shù)并不是線程安全的。

    C++中的構(gòu)造函數(shù)簡單來說分兩步:

    第一步:內(nèi)存分配

    第二步:初始化成員變量

    由于多線程的關(guān)系,可能當(dāng)我們在分配內(nèi)存好了以后,還沒來得急初始化成員變量,就
    進(jìn)行線程切換,另外一個線程拿到所有權(quán)后,由于內(nèi)存已經(jīng)分配了,但是變量初始化還
    沒進(jìn)行,因此打印成員變量的相關(guān)值會發(fā)生不一致現(xiàn)象。

    結(jié)論:Meyers方式雖然能確保在多線程中產(chǎn)生唯一的實例,但是不能確保成員變量的值是否正確.

Next:
下一篇我們來實現(xiàn)一個線程安全,無內(nèi)存泄漏,基于“懶漢”行為的單例模式。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容