• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            #ant

            The dreams in which I'm dying are the best I've ever had...

            非完美C++ Singleton實(shí)現(xiàn)[2]

            4.解決多線程問(wèn)題
            上一篇實(shí)現(xiàn)的Singleton只能在單線程環(huán)境中使用,在多線程環(huán)境中會(huì)出現(xiàn)很多問(wèn)題,看Instance()實(shí)現(xiàn)代碼:
            1?static?Singleton&?Instance()?{
            2?????if?(0?==?_instance)?{?//1

            3?????????_instance?=?new?Singleton();?//2
            4?????????atexit(Destroy);
            5?
            ????}
            6?????return?*_instance;?//3

            7?}
            考慮如下情況:線程一調(diào)用Instance(),進(jìn)入//1,0 == _instance 返回true,線程一于是進(jìn)入//2。這時(shí)候線程一被掛起,線程二開始執(zhí)行,線程二調(diào)用Instance(),進(jìn)入//1,發(fā)現(xiàn)0 == _instance 仍然返回true,線程二于是也進(jìn)入//2,線程二繼續(xù)執(zhí)行到//3直到返回。這時(shí)候線程一被喚醒,繼續(xù)從//2開始執(zhí)行,這將會(huì)覆蓋線程二創(chuàng)建的_instance,線程一繼續(xù)執(zhí)行到//3直到返回...

            解決方法很簡(jiǎn)單,引入相關(guān)同步對(duì)象(synchronization object)就行了,例如在win32平臺(tái)下可以如下實(shí)現(xiàn):
            synobj.h
            ?1?#ifndef?SYNOBJ_H
            ?2?
            #define?SYNOBJ_H
            ?3?

            ?4?#include?<windows.h>
            ?5?
            ?6?#define?CLASS_UNCOPYABLE(classname)?\
            ?7?????private
            :?\
            ?8?????classname(const?classname&
            );?\
            ?9?????classname&?operator=(const?classname&
            );
            10?

            11?class?Mutex?{
            12?
            ????CLASS_UNCOPYABLE(Mutex)
            13?public
            :
            14?????Mutex()?:_cs()?{?InitializeCriticalSection(&
            _cs);?}
            15?????~Mutex()?{?DeleteCriticalSection(&
            _cs);?}
            16?????void?lock()?{?EnterCriticalSection(&
            _cs);?}
            17?????void?unlock()?{?LeaveCriticalSection(&
            _cs);?}
            18?private
            :
            19?
            ????CRITICAL_SECTION?_cs;
            20?
            };
            21?

            22?class?Lock?{
            23?
            ????CLASS_UNCOPYABLE(Lock)
            24?public
            :
            25?????explicit?Lock(Mutex&
            ?cs)?:_cs(cs)?{?_cs.lock();?}
            26?????~
            Lock()?{?_cs.unlock();?}
            27?private
            :
            28?????Mutex&
            ?_cs;
            29?
            };
            30?

            31?#endif/*SYNOBJ_H*/

            有了同步對(duì)象很容易就能夠?qū)懗鋈缦麓a:
            singleton.h
            ?1?#ifndef?SINGLETON_H
            ?2?
            #define?SINGLETON_H
            ?3?

            ?4?#include?"synobj.h"
            ?5?
            ?6?class?Singleton?{
            ?7?public
            :
            ?8?????static?Singleton&?Instance()?{?//?Unique?point?of?access

            ?9?????????Lock?lock(_mutex);
            10?????????if?(0?==
            ?_instance)?{
            11?????????????_instance?=?new
            ?Singleton();
            12?????????????atexit(Destroy);?//?Register?Destroy?function

            13?????????}
            14?????????return?*
            _instance;
            15?
            ????}
            16?????void
            ?DoSomething(){}
            17?private
            :
            18?????static?void?Destroy()?{?//?Destroy?the?only?instance

            19?????????if?(?_instance?!=?0?)?{
            20?
            ????????????delete?_instance;
            21?????????????_instance?=?0
            ;
            22?
            ????????}
            23?
            ????}
            24?????Singleton(){}?//?Prevent?clients?from?creating?a?new?Singleton

            25?????~Singleton(){}?//?Prevent?clients?from?deleting?a?Singleton
            26?????Singleton(const?Singleton&);?//?Prevent?clients?from?copying?a?Singleton
            27?????Singleton&?operator=(const?Singleton&);
            28?private
            :
            29?????static
            ?Mutex?_mutex;
            30?????static?Singleton?*_instance;?//?The?one?and?only?instance

            31?};
            32?

            33?#endif/*SINGLETON_H*/

            singleton.cpp
            1?#include?"singleton.h"
            2?
            3?Mutex?Singleton::_mutex;
            4?Singleton*?Singleton::_instance?=?0;
            現(xiàn)在的Singleton雖然多線程安全,性能卻受到了影響。從Instance()中可以看到,實(shí)際上僅僅當(dāng)0 == _instance為true時(shí)才需要Lock。你很容易就寫出如下代碼:
            1?static?Singleton&?Instance()?{
            2?????if?(0?==
            ?_instance)?{
            3?
            ????????Lock?lock(_mutex);
            4?????????_instance?=?new
            ?Singleton();
            5?
            ????????atexit(Destroy);
            6?
            ????}
            7?????return?*
            _instance;
            8?}
            但是這樣還是會(huì)產(chǎn)生競(jìng)爭(zhēng)條件(race condition),一種廣為人知的做法是使用所謂的Double-Checked Locking:
            ?1?static?Singleton&?Instance()?{
            ?2?????if?(0?==
            ?_instance)?{
            ?3?
            ????????Lock?lock(_mutex);
            ?4?????????if?(0?==
            ?_instance)?{
            ?5?????????????_instance?=?new
            ?Singleton();
            ?6?
            ????????????atexit(Destroy);
            ?7?
            ????????}
            ?8?
            ????}
            ?9?????return?*
            _instance;
            10?}
            Double-Checked Locking機(jī)制看起來(lái)像是一個(gè)完美的解決方案,但是在某些條件下仍然不行。簡(jiǎn)單的說(shuō),編譯器為了效率可能會(huì)重排指令的執(zhí)行順序(compiler-based reorderings)。看這一行代碼:

            _instance?=?new?Singleton();

            在編譯器未優(yōu)化的情況下順序如下:
            1.new operator分配適當(dāng)?shù)膬?nèi)存;
            2.在分配的內(nèi)存上構(gòu)造Singleton對(duì)象;
            3.內(nèi)存地址賦值給_instance。


            但是當(dāng)編譯器優(yōu)化后執(zhí)行順序可能如下:
            1.new operator分配適當(dāng)?shù)膬?nèi)存;
            2.內(nèi)存地址賦值給_instance;
            3.在分配的內(nèi)存上構(gòu)造Singleton對(duì)象。


            當(dāng)編譯器優(yōu)化后,如果線程一執(zhí)行到2后被掛起。線程二開始執(zhí)行并發(fā)現(xiàn)0 == _instance為false,于是直接return,而這時(shí)Singleton對(duì)象可能還未構(gòu)造完成,后果...

            上面說(shuō)的還只是單處理器的情況,在多處理器(multiprocessors)的情況下,超線程技術(shù)必然會(huì)混合執(zhí)行指令,指令的執(zhí)行順序更無(wú)法保障。關(guān)于Double-Checked Locking的更詳細(xì)的文章,請(qǐng)看:
            The "Double-Checked Locking is Broken" Declaration

            5.使用volatile關(guān)鍵字
            為了說(shuō)明問(wèn)題,請(qǐng)先考慮如下代碼:
            ?1?class?MyThread?:?public?Thread?{
            ?2?public
            :
            ?3?????virtual?void
            ?run()?{
            ?4?????????while?(!
            _stopped)?{
            ?5?????????????//do?something

            ?6?????????}
            ?7?
            ????}
            ?8?????void
            ?stop()?{
            ?9?????????_stopped?=?true
            ;
            10?
            ????}
            11?private
            :
            12?
            ????bool?_stopped;
            13?
            };
            14?

            15?...
            16?
            17?MyThread?thread;
            18?thread.start();
            上面用thread.start()開啟了一個(gè)線程,該線程在while循環(huán)中檢測(cè)bool標(biāo)記_stopped,看是否該繼續(xù)執(zhí)行。如果想要結(jié)束這個(gè)線程,調(diào)用thread.stop()應(yīng)該沒(méi)問(wèn)題。但是需要注意的是編譯器很有可能對(duì)_stopped的存取進(jìn)行優(yōu)化。如果編譯器發(fā)現(xiàn)_stopped被頻繁存取(_stopped在while循環(huán)中),編譯器可能會(huì)考慮將_stopped緩存到寄存器中,以后_stopped將會(huì)直接從寄存器存取。這時(shí)候如果某個(gè)線程調(diào)用了thread.stop(),對(duì)_stopped的修改將不會(huì)反映到寄存器中,thread將會(huì)永遠(yuǎn)循環(huán)下去...

            為了防止編譯器優(yōu)化,用volatile關(guān)鍵字就OK了,volatile跟const的用法幾乎一樣,能用const的地方也都能用volatile。對(duì)Singleton來(lái)說(shuō),修改如下兩處即可:
            1?//singleton.h中
            2?static?Singleton?*_instance;
            3?//改為

            4?static?Singleton?*?volatile?_instance;
            5?

            6?//singleton.cpp中
            7?Singleton*?Singleton::_instance?=?0;
            8?//改為

            9?Singleton*?volatile?Singleton::_instance?=?0;


            6.將Singleton泛化為模板
            singleton.h
            ?1?#ifndef?SINGLETON_H
            ?2?
            #define?SINGLETON_H
            ?3?

            ?4?#include?"synobj.h"
            ?5?
            ?6?template<class?T>
            ?7?class?Singleton?{
            ?8?
            ????CLASS_UNCOPYABLE(Singleton)
            ?9?public
            :
            10?????static?T&?Instance()?{?//?Unique?point?of?access

            11?????????if?(0?==?_instance)?{
            12?
            ????????????Lock?lock(_mutex);
            13?????????????if?(0?==
            ?_instance)?{
            14?????????????????_instance?=?new
            ?T();
            15?
            ????????????????atexit(Destroy);
            16?
            ????????????}
            17?
            ????????}
            18?????????return?*
            _instance;
            19?
            ????}
            20?protected
            :
            21?
            ????Singleton(){}
            22?????~
            Singleton(){}
            23?private
            :
            24?????static?void?Destroy()?{?//?Destroy?the?only?instance

            25?????????if?(?_instance?!=?0?)?{
            26?
            ????????????delete?_instance;
            27?????????????_instance?=?0
            ;
            28?
            ????????}
            29?
            ????}
            30?????static
            ?Mutex?_mutex;
            31?????static?T?*?volatile?_instance;?//?The?one?and?only?instance

            32?};
            33?

            34?template<class?T>
            35?Mutex?Singleton<T>::_mutex;
            36?

            37?template<class?T>
            38?T?*?volatile?Singleton<T>::_instance?=?0;
            39?

            40?#endif/*SINGLETON_H*/

            測(cè)試代碼:
            test.cpp
            ?1?#include?"singleton.h"
            ?2?
            ?3?class?A?:?public?Singleton<A>?{
            ?4?????friend?class?Singleton<A>
            ;
            ?5?protected
            :
            ?6?
            ????A(){}
            ?7?????~
            A(){}
            ?8?public
            :
            ?9?????void
            ?DoSomething(){}
            10?
            };
            11?

            12?int?main()?{
            13?

            14?????A?&a?=?A::Instance();
            15?
            ????a.DoSomething();
            16?

            17?????return?0;
            18?}


            7.Singleton的析構(gòu)問(wèn)題
            到此Singleton已經(jīng)算比較完善了,但是依然算不上完美,因?yàn)榈浆F(xiàn)在只是解決了多線程問(wèn)題,加入了模板支持,對(duì)于KDL problem(The Dead Reference Problem)依然沒(méi)法解決,可以說(shuō)在實(shí)現(xiàn)Singleton模式時(shí),最大的問(wèn)題就是多個(gè)有依賴關(guān)系的Singleton的析構(gòu)順序。雖然Modern C++ Design中給出了解決方案,但是Loki的實(shí)現(xiàn)太過(guò)復(fù)雜,在此就不詳細(xì)說(shuō)明了,有興趣的可以看看Modern C++ Design,當(dāng)然了,Loki庫(kù)中用策略模式實(shí)現(xiàn)的Singleton也很不錯(cuò)!

            posted on 2007-09-07 23:22 螞蟻終結(jié)者 閱讀(5054) 評(píng)論(13)  編輯 收藏 引用 所屬分類: Design Pattern

            Feedback

            # re: 非完美C++ Singleton實(shí)現(xiàn)[2] 2007-09-08 09:12 sneaker

            "The Dead Reference Problem"確實(shí)是實(shí)現(xiàn)Singleton的首要問(wèn)題  回復(fù)  更多評(píng)論   

            # re: 非完美C++ Singleton實(shí)現(xiàn)[2] 2007-09-09 03:25 func

            華麗的推演,“裱”成PDF收藏~期待下篇  回復(fù)  更多評(píng)論   

            # re: 非完美C++ Singleton實(shí)現(xiàn)[2] 2007-09-10 11:14 ChenA

            volatile也解決不了多線程的問(wèn)題,詳情請(qǐng)看C++ and the Perils of Double-Checked Locking。
            KDL的問(wèn)題設(shè)計(jì)成不依賴不就行了,需要依賴關(guān)系的手動(dòng)釋放,這是最簡(jiǎn)單的辦法。  回復(fù)  更多評(píng)論   

            # re: 非完美C++ Singleton實(shí)現(xiàn)[2] 2007-09-10 11:33 螞蟻終結(jié)者

            @ChenA
            thanks!  回復(fù)  更多評(píng)論   

            # re: 非完美C++ Singleton實(shí)現(xiàn)[2] 2007-09-19 14:24 func

            我的理解,volatile雖然不能解決多線程的很多問(wèn)題,但這里的使用應(yīng)該解決了問(wèn)題.  回復(fù)  更多評(píng)論   

            # re: 非完美C++ Singleton實(shí)現(xiàn)[2] 2007-10-09 14:33 fr3@K

            已知 : global (static) instance 在 multithreading 下有啟始上的問(wèn)題。
            試問(wèn) : 如何用一個(gè) global instance (mutex) 去確保另一個(gè) global instance 的啟始正確?

            請(qǐng)參考拙著 Is your Singleton Broken? (http://fsfoundry.org/codefreak/2006/05/05/is-your-singleton-broken/)  回復(fù)  更多評(píng)論   

            # re: 非完美C++ Singleton實(shí)現(xiàn)[2] 2007-10-09 16:30 螞蟻終結(jié)者

            @fr3@K
            實(shí)際上multithreading的問(wèn)題不在于mutex這里,一個(gè)好的設(shè)計(jì)會(huì)在main函數(shù)真正啟動(dòng)后再調(diào)用Instance(),而這時(shí)候global object可以確保已經(jīng)初始化,即在調(diào)用Instance()時(shí)可以保證mutex已經(jīng)初始化。因此只要程序在真正進(jìn)入main函數(shù)以前不調(diào)用Instance(),就不會(huì)有g(shù)lobal instance的初始化問(wèn)題。  回復(fù)  更多評(píng)論   

            # re: 非完美C++ Singleton實(shí)現(xiàn)[2] 2007-10-09 16:32 螞蟻終結(jié)者

            其實(shí)后來(lái)覺(jué)得多數(shù)情況下eager initialization要優(yōu)于lazy initialization。
            如經(jīng)典的Meyer's Singleton以及Boost::singleton
              回復(fù)  更多評(píng)論   

            # re: 非完美C++ Singleton實(shí)現(xiàn)[2] 2007-10-09 16:42 王博煒

            強(qiáng)  回復(fù)  更多評(píng)論   

            # re: 非完美C++ Singleton實(shí)現(xiàn)[2] 2007-10-09 21:00 fr3@K

            @螞蟻終結(jié)者

            我的考量多是站在 library writer (職業(yè)病) 的角度。
            Library 常需要在程式一 load 上來(lái)就把 static (global) instance 啟始好. 總不好要求使用者進(jìn)入 main() 才能使用.

            希望你能把進(jìn)入 main() 之后再呼叫 Instance() 這段加入你的文章里面, 以免誤導(dǎo)了讀者如我輩.

            我是盡量避免 lazy initialization 的人, 尤其是在 static instance (singleton) 的創(chuàng)建上. Lazy initialization 的策略有可能造成譬如說(shuō)一個(gè) global logger 在某個(gè)對(duì)象的虛構(gòu)函數(shù)內(nèi)第一次被使用 (或許可能性很小, 但畢竟不能 100% 排除), 而我們又無(wú)法保證該 logger 肯定會(huì)被創(chuàng)建成功. 這樣是否代表我們必須在每個(gè)使用到 logger 的 destructor 內(nèi)部做 try-and-catch? 等等問(wèn)題...  回復(fù)  更多評(píng)論   

            # re: 非完美C++ Singleton實(shí)現(xiàn)[2] 2007-10-10 09:06 螞蟻終結(jié)者

            @fr3@K
            謝謝你的建議。
            不能從static code中調(diào)用Instance()確實(shí)是一種限制。我想應(yīng)該可以通過(guò)某種類似Boost::singleton中的技巧來(lái)確保static object(mutex)在Instance()之前初始化。有時(shí)間了再把這段內(nèi)容補(bǔ)上。  回復(fù)  更多評(píng)論   

            # re: 非完美C++ Singleton實(shí)現(xiàn)[2] 2008-05-06 09:21 sodar

            非常好的文章,學(xué)習(xí)了

            可是遇到一個(gè)問(wèn)題,如果我是在動(dòng)態(tài)庫(kù)工程中使用這個(gè)Singleton,因?yàn)閍texit不能用于DLL,這種實(shí)現(xiàn)是不是會(huì)有問(wèn)題呢?  回復(fù)  更多評(píng)論   

            # re: 非完美C++ Singleton實(shí)現(xiàn)[2] 2008-05-06 22:02 螞蟻終結(jié)者

            @sodar
            能說(shuō)下在動(dòng)態(tài)庫(kù)工程中使用Singleton的具體需求嗎?dll中用atexit注冊(cè)的函數(shù)在FreeLibrary的時(shí)候會(huì)被調(diào)用,可以保證Singleton安全析構(gòu)。  回復(fù)  更多評(píng)論   

            # re: 非完美C++ Singleton實(shí)現(xiàn)[2] 2012-05-28 08:26 SYBILRobbins

            This is known that cash can make us autonomous. But what to do when somebody does not have money? The only one way is to try to get the <a href="http://goodfinance-blog.com/topics/business-loans">business loans</a> and financial loan.   回復(fù)  更多評(píng)論   

            久久久久波多野结衣高潮| 色综合久久久久无码专区 | 久久人妻少妇嫩草AV蜜桃| 久久综合伊人77777麻豆| 综合久久国产九一剧情麻豆| 日本福利片国产午夜久久| 久久久久久亚洲精品不卡 | 久久香蕉综合色一综合色88| 伊人久久久AV老熟妇色| 久久国产精品99久久久久久老狼| 国产精品熟女福利久久AV| 久久久久久亚洲精品影院| 久久国产精品77777| 久久久久亚洲AV成人网| 久久久久久精品免费免费自慰| 嫩草伊人久久精品少妇AV| 一本综合久久国产二区| 成人综合伊人五月婷久久| 伊人久久大香线蕉综合影院首页| 国产一区二区三区久久| 日本精品一区二区久久久| 99久久综合狠狠综合久久| 性做久久久久久久| 少妇熟女久久综合网色欲| 2021国产精品午夜久久| 久久亚洲高清观看| 99久久99这里只有免费费精品| 亚洲欧美一区二区三区久久| 久久丫精品国产亚洲av| 精品国产99久久久久久麻豆| 久久精品中文字幕第23页| 久久精品国产一区二区| 久久精品国产欧美日韩| 国产免费久久久久久无码| 色综合久久中文色婷婷| 久久青青草原综合伊人| 久久精品国产一区| 国产成人AV综合久久| 久久这里只有精品视频99| 女同久久| 久久久久亚洲AV无码专区首JN |