偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

細(xì)說(shuō)Singleton模式:創(chuàng)建、多線程與銷毀

開發(fā) 后端
本文對(duì)Singleton模式進(jìn)行了一番簡(jiǎn)略的介紹,主要參考了GOF著作中對(duì)Singleton的描述。GOF著作中對(duì)Singleton模式的描述為:保證一個(gè)class只有一個(gè)實(shí)體(Instance),并為它提供一個(gè)全局訪問(wèn)點(diǎn)(global access point)。

GOF著作中對(duì)Singleton模式的描述為:保證一個(gè)class只有一個(gè)實(shí)體(Instance),并為它提供一個(gè)全局訪問(wèn)點(diǎn)(global access point)。

從其描述來(lái)看,是非常簡(jiǎn)單的,但實(shí)現(xiàn)該模式卻是復(fù)雜的。Singleton設(shè)計(jì)模式不存在一種所謂的“***”方案。需要根據(jù)當(dāng)時(shí)的具體問(wèn)題進(jìn)行具體解決,下面將講述在不同環(huán)境下的解決方案。

Singleton的詳細(xì)解釋,請(qǐng)大家看GOF的著作《設(shè)計(jì)模式》一書。俺比較懶,是不想抄了。

1         Singleton創(chuàng)建

1.1      GOF Singleton

在GOF著作中對(duì)Singleton模式的實(shí)現(xiàn)方式如下:

  1. /*解一*/   
  2. class Singleton   
  3. {   
  4. public:   
  5. static Singleton *Instance(){                            //1   
  6. if( !m_pInstatnce) //2   
  7. m_pInstance = new Singleton;//3   
  8. return m_pInstance; //4   
  9. }   
  10. private:   
  11. static Singleton *m_pInstatnce;             //5   
  12. private:   
  13. Singleton();                                                         //6   
  14. Singleton(const Singleton&);                             //7   
  15. Singleton& operator=(const Singleton&);            //8   
  16. ~Singleton();                                                       //9   
  17. }   
  18. Singleton *Singleton:m_pInstatnce = NULL; //10   
  19.  

在上面的解決方案中,我們只在需要調(diào)用時(shí),才產(chǎn)生一個(gè)Singleton的對(duì)象。這樣帶來(lái)的好處是,如果該對(duì)象產(chǎn)生帶來(lái)的結(jié)果很昂貴,但不經(jīng)常用到時(shí),是一種非常好的策略。但如果該Instance被頻繁調(diào)用,那么就有人覺(jué)得Instance中的判斷降低了效率(雖然只是一個(gè)判斷語(yǔ)句^_^),那么我們就把第5條語(yǔ)句該為

  1. static Singleton m_Instatnce;  

如此一來(lái),在Instatnce直接返回&m_Instance,而不用做任何判斷,效率也高了。(是不是呢?)

這樣修改后,我們將帶來(lái)災(zāi)難性的后果:

1:首先有可能編譯器這關(guān)就沒(méi)法通過(guò),說(shuō)m_Instance該外部變量無(wú)法解決(visural C++6.0)

  1. error LNK2001: unresolved external symbol "private: static class Singleton  Singleton::m_Instance" (?m_Instance@Singleton@@0V1@A)  

2:如果編譯器這關(guān)通過(guò)了就沒(méi)問(wèn)題了么?答案是否定的。

***是不管Instance是否用到,該靜態(tài)變量對(duì)象在編譯器編譯時(shí)就產(chǎn)生了,即資源消耗是不可避免的;

第二是無(wú)法確保編譯器一定先將m_Instance初始化。所以Instance的調(diào)用有可能傳回一個(gè)尚沒(méi)構(gòu)造的Singleton對(duì)象。這也意味著你無(wú)法保證任何外部對(duì)象所使用的m_Instance是一個(gè)被正確初始化的對(duì)象。

1.2      Meyers Singleton
我們?nèi)绾谓鉀Q這個(gè)問(wèn)題呢,實(shí)際上很簡(jiǎn)單。一種非常優(yōu)雅的做法由Scott Meyers***提出,故也稱為Meyers Singleton。它依賴編譯器的神奇技巧。即函數(shù)內(nèi)的static對(duì)象只在該函數(shù)***次執(zhí)行時(shí)才初始化(請(qǐng)注意不是static常量)。

  1. /*解二*/   
  2. class Singleton   
  3. {   
  4. public:   
  5. static Singleton *Instance(){                            //1   
  6. static Singleton sInstance; //2   
  7. return &sInstance; //3   
  8. }   
  9. private:   
  10. Singleton();                                                         //4   
  11. Singleton(const Singleton&);                             //5   
  12. Singleton& operator=(const Singleton&);            //6   
  13. ~Singleton();                                                       //7   
  14. }   
  15.  

解二在Instance中定義了一個(gè)Static的Singleton對(duì)象,來(lái)解決Instance中初始化的問(wèn)題,也很順利的解決了定義Static成員對(duì)象帶來(lái)的問(wèn)題。

請(qǐng)注意,解二在VC6中不能編譯通過(guò),將有以下的錯(cuò)誤:

  1. error C2248: 'Singleton::~Singleton' : cannot access private member declared in class 'Singleton' e:\work\q\a.h(81) : see declaration of 'Singleton::~Singleton'  

產(chǎn)生該問(wèn)題的錯(cuò)誤原因是什么呢(請(qǐng)仔細(xì)思考^_^)

原因在于在產(chǎn)生static Singleton對(duì)象后,編譯器會(huì)自動(dòng)產(chǎn)生一個(gè)銷毀函數(shù)__DestroySingleton,然后調(diào)用atexit()注冊(cè),在程序退出時(shí)執(zhí)行__DestroySingleton。但由于Singleton的析構(gòu)函數(shù)是private,所以會(huì)產(chǎn)生訪問(wèn)錯(cuò)誤。(應(yīng)該在以后的編譯器中修改了該BUG)

1.3      Singleton改進(jìn)

讓Instance傳回引用(reference)。如果傳回指針,調(diào)用端有可能講它delete調(diào)。

1.4      Singleton注意之點(diǎn)

在上面的解法中,請(qǐng)注意對(duì)構(gòu)造函數(shù)和析構(gòu)函數(shù)的處理,有何好處(請(qǐng)自己理解,俺懶病又犯了)。

2         多線程

在解一中,如果我們運(yùn)行在多線程的環(huán)境中,該方案是***的么,將會(huì)有什么后果呢?

后果就是會(huì)造成內(nèi)存泄漏,并且有可能前后獲取的Singleton對(duì)象不一樣(原因請(qǐng)自己思考,后面有解答)。

為了解決這個(gè)問(wèn)題,將解一的Instance改為如下:

  1. Singleton& Singleton::Instance(){   
  2. Lock(m_mutex);            //含義為獲取互斥量            //1   
  3. If( !m_pInstance ){                                          //2   
  4. m_pInstance = new Singleton; //3   
  5. }   
  6. UnLock(m_mutex);                                            //4   
  7. return *m_pInstance;                                     //5   
  8. }   
  9.  

此種方法將解決解一運(yùn)行在多線程環(huán)境下內(nèi)存泄漏的問(wèn)題,但帶來(lái)的結(jié)果是,當(dāng)m_mutex被鎖定時(shí),其它試圖鎖定m_mutex的線程都將必須等等。并且每次執(zhí)行鎖操作其付出的代價(jià)極大,亦即是這種方案的解決辦法并不吸引人。

那么我們將上面的代碼改為如下方式:

  1. Singleton& Singleton::Instance(){   
  2. If( !m_pInstance ){                                                      //1   
  3. Lock(m_mutex); //含義為獲取互斥量 //2   
  4. m_pInstance = new Singleton; //3   
  5. UnLock(m_mutex); //4   
  6. }   
  7. return *m_pInstance;                                                 //5   
  8. }   
  9.  

這樣修改的結(jié)果沒(méi)有問(wèn)題了么?NO!!!!該方案帶來(lái)的結(jié)果同解一,原因也一樣,都將造成內(nèi)存泄漏。此時(shí)“雙檢測(cè)鎖定”模式就粉墨登場(chǎng)了。

由Doug Schmidt和Tim Harrison提出了“雙檢測(cè)鎖定”(Double-Checked Locking)模式來(lái)解決multithread singletons問(wèn)題。

  1. Singleton& Singleton::Instance(){   
  2. If( !m_pInstance ){                                                      //1   
  3. Lock(m_mutex); //含義為獲取互斥量 //2   
  4. If(!m_pInstance) //3   
  5. m_pInstance = new Singleton; //4   
  6. UnLock(m_mutex); //5   
  7. }   
  8. return *m_pInstance;                                                 //6   
  9. }  

請(qǐng)看上面的第三句,這句話是不是具有化腐朽為神奇的力量啊 ^_^

上面的方案就***了么?;卮疬€是NO!!!(各位看官是否已經(jīng)郁悶了啊,這不是玩我啊?請(qǐng)耐心點(diǎn),聽我細(xì)細(xì)到來(lái)^_^)

如果在RISC機(jī)器上編譯器有可能將上面的代碼優(yōu)化,在鎖定m_mutex前執(zhí)行第3句。這是完全有可能的,因?yàn)?**句和第3句一樣,根據(jù)代碼優(yōu)化原則是可以這樣處理的。這樣一來(lái),我們引以為自豪的“雙檢測(cè)鎖定”居然沒(méi)有起作用( L)

怎么辦?解決唄。怎么解決?簡(jiǎn)單,我們?cè)趍_pInstance前面加一個(gè)修飾符就可以了。什么修飾符呢?……

àvolatile(簡(jiǎn)單吧)

那么我們完整的解法如下:

  1. /*解三*/   
  2. class Singleton   
  3. {   
  4. public:   
  5. static Singleton &Instance(){                            //1   
  6. if( !m_pInstatnce){ //2   
  7. Lock(m_mutex) //3   
  8. If( !m_pInstance ) //4   
  9. m_pInstance = new Singleton;//5   
  10. UnLock(m_mutex); //6   
  11. }   
  12. return *m_pInstance; //7   
  13. }   
  14. private:   
  15. static volatitle Singleton *m_pInstatnce;            //8   
  16. private:   
  17. Singleton();                                                         //9   
  18. Singleton(const Singleton&);                             //10   
  19. Singleton& operator=(const Singleton&);            //11   
  20. ~Singleton();                                                       //12   
  21. }   
  22. Singleton *Singleton:m_pInstatnce = NULL; //13   
  23.  

3         Singleton銷毀

在這里,我們就到了Singleton最簡(jiǎn)單也最復(fù)雜的地方了。

為什么說(shuō)它簡(jiǎn)單?我們根本可以不理睬創(chuàng)建的對(duì)象m_pInstance的銷毀啊。因?yàn)殡m然我們一直沒(méi)有將Singleton對(duì)象刪除,但不會(huì)造成內(nèi)存泄漏。為什么這樣說(shuō)呢?因?yàn)橹挥挟?dāng)你分配了累積行數(shù)據(jù)并丟失了對(duì)他的所有reference是,內(nèi)存泄漏才發(fā)生。而對(duì)Singleton并不屬于上面的情況,沒(méi)有累積性的東東,而且直到結(jié)束我們還有它的引用。在現(xiàn)代操作系統(tǒng)中,當(dāng)一個(gè)進(jìn)程結(jié)束后,將自動(dòng)將該進(jìn)程所有內(nèi)存空間完全釋放。(可以參考《effective C++》條款10,里面講述了內(nèi)存泄漏)。

但有時(shí)泄漏還是存在的,那是什么呢?就是資源泄漏。比如說(shuō)如果該Singleton對(duì)象管理的是網(wǎng)絡(luò)連接,OS互斥量,進(jìn)程通信的handles等等。這時(shí)我們就必須考慮到Singleton的銷毀了。談到銷毀,那可是一個(gè)復(fù)雜的課題(兩天三夜也說(shuō)不完^_^  開玩笑的啦,大家輕松一下嘛)。

我們需要在恰當(dāng)?shù)牡攸c(diǎn),恰當(dāng)?shù)臅r(shí)機(jī)刪除Singleton對(duì)象,并且還要在恰當(dāng)?shù)臅r(shí)機(jī)創(chuàng)建或者重新創(chuàng)建Singleton對(duì)象。

在我們的“解二”中,在程序結(jié)束時(shí)會(huì)自動(dòng)調(diào)用Singleton的析構(gòu)函數(shù),那么也將自動(dòng)釋放所獲取的資源。在大多數(shù)情況下,它都能夠有效運(yùn)作。那特殊情況是什么呢?

我們以KDL(keyboard,display,log)模型為例,其中K,D,L均使用Singleton模式。只要keyboard或者display出現(xiàn)異常,我們就必須調(diào)用log將其寫入日志中,否則log對(duì)象不應(yīng)該創(chuàng)建。對(duì)后面一條,我們的Singleton創(chuàng)建時(shí)就可以滿足。

在前面我們已經(jīng)說(shuō)到,在產(chǎn)生一個(gè)對(duì)象時(shí)(非用new產(chǎn)生的對(duì)象),由編譯器自動(dòng)調(diào)用了atexit(__DestroyObject)函數(shù)來(lái)實(shí)現(xiàn)該對(duì)象的析構(gòu)操作。而C++對(duì)象析構(gòu)是LIFO進(jìn)行的,即先產(chǎn)生的對(duì)象后摧毀。

如果在一般情況下調(diào)用了log對(duì)象,然后開始銷毀對(duì)象。按照“后創(chuàng)建的先銷毀”原則:log對(duì)象將被銷毀,然后display對(duì)象開始銷毀。此時(shí)display在銷毀發(fā)現(xiàn)出現(xiàn)異常,于是調(diào)用log對(duì)象進(jìn)行記錄。但事實(shí)上,log對(duì)象已經(jīng)被銷毀,那么調(diào)用log對(duì)象將產(chǎn)生不可預(yù)期的后果,此問(wèn)題我們稱為Dead Reference。所以前面的解決方案不能解決目前我們遇到的問(wèn)題。

Andrei Alexandrescu提出了解決方案,稱為Phoenix Singleton(取自鳳凰涅磐典故)

  1. /*解四*/   
  2. class Singleton   
  3. {   
  4. public:   
  5. static Singleton &Instance(){                              
  6. if( !m_pInstatnce){   
  7. Lock(m_mutex)   
  8. If( !m_pInstance ){   
  9. if(m_destroyed)   
  10. OnDeadReference();   
  11. else   
  12. Create();   
  13. }   
  14. UnLock(m_mutex);   
  15. }   
  16. return *m_pInstance;   
  17. }   
  18. private:   
  19. static volatitle Singleton *m_pInstatnce;   
  20. static bool m_destroyed;   
  21. private:   
  22. Singleton();                                                           
  23. Singleton(const Singleton&);                               
  24. Singleton& operator=(const Singleton&);       
  25. ~Singleton(){   
  26. m_pInstance = 0;   
  27. m_destroyed = true;   
  28. }   
  29. static void Create(){   
  30. static Singleton sInstance;   
  31. m_pInstanace = &sInstance;   
  32. }   
  33. static void OnDeadReference(){   
  34. Create();   
  35. new (m_pInstance) Singleton;   
  36. atexit(KillPhoenixSingleton);   
  37. m_destroyed = false;   
  38. }   
  39. void KillPhoenixSingleton(){   
  40. m_pInstance->~Singleton();   
  41. }   
  42. }   
  43. Singleton *Singleton:m_pInstatnce = NULL;   
  44. bool m_destroyed =false;    
  45.  

請(qǐng)注意此處OnDeadReference()中所使用的new操作符的用法:是所謂的placement new操作,它并不分配內(nèi)存,而是在某個(gè)地址上構(gòu)造一個(gè)新對(duì)象。

這是解決Dead Reference方法之一。如果此時(shí)keyboard或者display對(duì)象也需要處理Dead Reference問(wèn)題時(shí),那么上面的OnDeadReference將被頻繁調(diào)用,效率將會(huì)很低。即該問(wèn)題為:需要提供一種解決方案,用于處理對(duì)象的建立過(guò)程可以不按照“先創(chuàng)建會(huì)銷毀”的原則,而應(yīng)該為其指定一個(gè)銷毀順序。

聰明的Andrei Alexandrescu提出了一個(gè)“帶壽命的Singleton”解決方案。該方案的思想是:利用atexit()的特性;在每次創(chuàng)建一個(gè)對(duì)象后,將該對(duì)象放入到一個(gè)鏈表中(該鏈表是按照銷毀順序排訓(xùn)的),并同時(shí)調(diào)用atexit()注冊(cè)一個(gè)銷毀函數(shù);該銷毀函數(shù)從鏈表中獲取最需要銷毀的對(duì)象進(jìn)行銷毀。(懶病又犯了L。該模式的實(shí)現(xiàn)請(qǐng)各位看官自行實(shí)現(xiàn),可以參考《C++設(shè)計(jì)新思維》一書,Andrei Alexandrescu著)。

各位看官,看完本篇后,是否覺(jué)得Singleton還簡(jiǎn)單啦 :)

【編輯推薦】

  1. 有關(guān)雙檢測(cè)鎖定(DCL)和Singleton模式的問(wèn)題
  2. 最簡(jiǎn)單的設(shè)計(jì)模式學(xué)習(xí):Singleton模式
  3. C# Singleton的使用及優(yōu)缺點(diǎn)探討
  4. 詳解Java Singleton模式的好處
  5. Scala程序中的分號(hào)推斷和Singleton對(duì)象
責(zé)任編輯:yangsai 來(lái)源: CSDN
相關(guān)推薦

2013-05-23 15:59:00

線程池

2023-06-06 08:17:52

多線程編程Thread類

2010-03-15 17:56:23

Java多線程

2009-08-31 16:12:02

C#使用Singlet

2025-06-10 09:07:32

Go模式開發(fā)

2010-02-01 17:18:23

Python多線程環(huán)境

2009-07-09 17:30:59

Singleton模式C++ SingletJava Single

2009-06-29 18:00:05

Java多線程Runnable接口創(chuàng)建線程

2009-06-29 17:54:10

Java多線程Thread類創(chuàng)建線程

2021-02-25 15:58:46

C++線程編程開發(fā)技術(shù)

2009-08-25 18:04:30

C#實(shí)現(xiàn)Singlet

2009-08-31 15:48:02

C# Singleto

2009-09-02 16:23:27

C# Singleto

2009-07-08 17:25:05

Java Single

2011-11-18 10:50:25

設(shè)計(jì)模式Java線程

2025-05-12 04:00:01

2010-03-10 08:54:49

Python多線程

2022-05-20 10:20:17

Spring事務(wù)MyBatis

2011-07-18 16:51:51

Cocoa 單態(tài) 模式

2012-08-22 10:10:25

單態(tài)單態(tài)設(shè)計(jì)設(shè)計(jì)模式
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)