C++中實現單例,需要注意兩個問題:1.對記憶體的釋放。2.多線程下的健壯性。一下是CSDN上對於此問題的討論,先存一個。
|
#9樓 得分:5回複於:2008-02-18 20:26:40
你這個方法實現singleton不太好,會有多線程同步問題。 比如說一開始Singleton::instance為空白。然後有線程A和線程B同時訪問Singleton::GetInstance()。 有意思的是當任務A執行if (NULL == instance)這句時,剛判斷好instance確實為空白想調用new,或者時間可以再放款到正在執行new操作,但是還沒有返回;要命的是調度器由於某些原因一下子剝奪了線程A的執行又去執行線程B了。線程B順利地建立了一個instance執行個體,然後在某一時刻被調度器剝奪,再次執行線程A。OK,此時線程A從剛才的混沌中繼續執行它的new Singleton()的操作,這樣線程A所建立出來的就是另一個執行個體了。此時Singleton宣告破滅。 因此,能夠迎合多線程,乃至多處理的單件處理可以用以下方式:
-
C/C++ code
-
class Singleton { private: static Singleton instance; Singleton(void) {} public: static Singleton& GetInstance() { return instance; }};Singleton Singleton::instance;
由於以上代碼中,Singleton::instance在程式載入後由初始化程式建立,因此對於應用程式不會有多線程困擾。如果是在多處理器或多核處理器環境下還要注意Singleton的成員函數中的一些同步。 |
|
|
|
#10樓 得分:0回複於:2008-02-18 20:35:14
回複Jim_King_2000: 在C++中使用singleton不用new主要不是因為要擔心如何delete。 如果你的應用程式指明了一個接收到一個訊息,由專門一個任務相應析構這個singleton執行個體,可以顯式地調用其解構函式:Singleton::GetInstance().~Singleton(); 個人認為多任務機制下,用樓主的方法不太合適。而Jim_King_2000你的方法也有多線程問題。 因為局部static也不會對執行個體建立的操作原子化,除非建構函式內部具有鎖機制。或者也是通過某種訊息相應或通過狀態表示要建立一個Singleton執行個體,以確保在第一次調用Singleton::GetInstance()時只有一個任務執行,且操作過程中不被打斷,至少被打斷後不會被重入。 |
|
|
|
#14樓 得分:0回複於:2008-02-19 08:40:39
感謝各位的精彩回複,讓在下獲益匪淺啊。 liuworld說得確實是,靜態成員變數必須要在具體的cpp檔案中進行定義,類中出現的靜態成員變數只表示一種聲明。 感謝zenny-chen的提醒,我這種寫法在多線程環境下確實會有潛在問題,這是一種“惡漢式”的單例模式,就是需要的時候才建立執行個體,你提供的實現是一種“懶漢式”的單例模式,就是程式初始化載入的時候就首先建立好執行個體,然後每次引用即可。這確實可以解決一些多線程調度的問題,但是會提前佔用系統資源,當然,一般來說,“惡漢式”的單例模式已經基本上可以滿足我們的需求了。 對於“懶漢式”的單例模式,可以使用鎖和雙重判斷的方式來避免重複建立的問題。 例如: static Singleton *GetInstance() { if (NULL == instance) { lock() { if (instance == NULL) instance = new Singleton(); } } return instance; } |
|
|
|
#15樓 得分:0回複於:2008-02-19 08:56:27
還是去看《Modern C++ Design》吧。 另外,google "Pattern Hatching",等,純C、C++語言是無法安全實現多安全執行緒的singleton的。 勉強夠用的實現就可以了。 |
|
|
|
#16樓 得分:0回複於:2008-02-19 11:08:55
to zenny_chen: 你的方法有效率問題。只要使用了你的類,這個靜態對象都會被建立,無論該對象是否被使用者使用。如果該對象的構造和析構十分費時的話,這樣的方法會使效率大大降低。況且你的方法也並不能保證多安全執行緒。你怎麼知道靜態instance在構造的時候沒有別的線程正在調用GetInstance函數? =========================================================================== 如果你的應用程式指明了一個接收到一個訊息,由專門一個任務相應析構這個singleton執行個體,可以顯式地調用其解構函式:Singleton::GetInstance().~Singleton(); =========================================================================== 如果應用程式沒有用訊息怎麼辦?就算使用了訊息,你怎麼知道其它線程不需要Singleton對象了?萬一程式非正常退出怎麼辦(比如遇到未處理的異常或者非法操作)? =========================================================================== 而Jim_King_2000你的方法也有多線程問題。 =========================================================================== 1、我真的很希望這是我的方法。可惜現實不是這樣。這個方法屬於Scott Meyers。 2、這個方法是實現singleton的基礎。很多種singleton實現(如相互依賴的singleton,多線程下的singleton等等)都是依據這個方法實現的。 3、很多實現在一開始的時候並不考慮多線程。等到單線程的實現方法出來以後,再擴充到多線程上面或者由使用者自行加鎖。STL也並不是多線程的。但這並不影響使用,你自己加鎖就是了。 所以說singleton裡面不用new並不是因為多線程。而是沒有必要用new。建議你也翻翻《Modern C++ Design》。裡面有多線程singleton的實現。 |
|
|
|
#17樓 得分:0回複於:2008-02-19 20:19:58
To 樓上: 呵呵,在大多數場合我要讓應用程式一直使用這個Singleton執行個體。因此,我將它放在全域資料儲存區是個不錯的主意,尤其在嵌入式系統中甚至可以將它放入ROM中。因此,在嵌入式系統中,這個執行個體在機器引導後就存在了,直到複位或關機。所以你說我還要對它在程式運行時進行建立或析構就顯得荒謬了。 而本人沒那麼多時間去讀那些號稱為C++大師們寫的東西,我感興趣的部分是基於多核處理器的作業系統以及並行計算,和人工智慧等問題。 |
|
|
|
#18樓 得分:0回複於:2008-02-20 13:53:25
to 樓上: ========================================== 在大多數場合我要讓應用程式一直使用這個Singleton執行個體 ========================================== 我們不應該假設這樣的場合。你的assumption不具有通用性。如果使用者的程式需要運行時建立singleton或者根本不建立某個singleton對象會怎樣?如果使用者只需要建立幾個不同的singleton對象中的一個或幾個(非全部)會怎麼樣? ========================================== 尤其在嵌入式系統中甚至可以將它放入ROM中 ========================================== 同樣,這仍然不具有通用性。Singleton是可以根據需要建立的,這樣會大大限制了singleton的使用場合。 ========================================== 因此,在嵌入式系統中,這個執行個體在機器引導後就存在了,直到複位或關機。所以你說我還要對它在程式運行時進行建立或析構就顯得荒謬了。 ========================================== 我們不應該假設這樣的場合,使用者的代碼不一定運行在嵌入式系統上。就算是在嵌入式系統中,singleton仍然可以根據需要建立,Scott Meyers的方法也適用,我們只要在應用程式開始的時候調用初始化函數(也就是包含singleton對象的定義的那個函數)即可。 ========================================== 而本人沒那麼多時間去讀那些號稱為C++大師們寫的東西 ========================================== 真沒時間就算了,如果有時間的話,還是要看一下。我們學數學,看得就是大師們的成果;你學人工智慧,書上的內容也是大師的成果;學C++也一樣。 |
|
|
|
#19樓 得分:0回複於:2008-02-20 13:53:44
to 樓上: ========================================== 在大多數場合我要讓應用程式一直使用這個Singleton執行個體 ========================================== 我們不應該假設這樣的場合。你的assumption不具有通用性。如果使用者的程式需要運行時建立singleton或者根本不建立某個singleton對象會怎樣?如果使用者只需要建立幾個不同的singleton對象中的一個或幾個(非全部)會怎麼樣? ========================================== 尤其在嵌入式系統中甚至可以將它放入ROM中 ========================================== 同樣,這仍然不具有通用性。Singleton是可以根據需要建立的,這樣會大大限制了singleton的使用場合。 ========================================== 因此,在嵌入式系統中,這個執行個體在機器引導後就存在了,直到複位或關機。所以你說我還要對它在程式運行時進行建立或析構就顯得荒謬了。 ========================================== 我們不應該假設這樣的場合,使用者的代碼不一定運行在嵌入式系統上。就算是在嵌入式系統中,singleton仍然可以根據需要建立,Scott Meyers的方法也適用,我們只要在應用程式開始的時候調用初始化函數(也就是包含singleton對象的定義的那個函數)即可。 ========================================== 而本人沒那麼多時間去讀那些號稱為C++大師們寫的東西 ========================================== 真沒時間就算了,如果有時間的話,還是要看一下。我們學數學,看得就是大師們的成果;你學人工智慧,書上的內容也是大師的成果;學C++也一樣。 |
|
|
|
#20樓 得分:0回複於:2008-02-21 18:27:14
To Jim_King_2000 : 實際上在你的代碼中,由於使用了static,儘管在函數內,但是在載入器載入你的應用程式時仍然會為你的全域唯一的Singleton預留空間。只不過在調用GetInstance()時才調用建構函式(若有的話)。 而我的方法是在載入應用程式後,在初始化代碼中調用建構函式(如果有的話),但卻帶來了額外的安全性。實際上你所謂的儲存空間效率兩者是一樣的,而且結束生命的時間也一樣。除非你在建構函式中調用了記憶體動態分配函數,這樣很顯然,My Code將在初始化過程中分配空間;而你的將在第一次調用GetInstance()時分配。 |
|
|
|
#25樓 得分:0回複於:2008-02-22 13:44:54
我想這裡的 Singleton 是針對語言本身提出的解決方案;如果將之與系統相關的設計聯絡起來,未免有些牽強了。 當然實際應用的時候首先考慮的是系統的設計,然後才是具體的解決方案,而這裡的Singleton ,正是在C++中的 解決方案之一。和系統相關的設計應該被分離出去。設計首先要保證 Singleton 的應用是系統安全的,不管是 在何種環境下,這個條件成立以後,才會提到它的具體實現的設計——比如說這裡的C++ Singleton 。否則,會把 很多問題纏繞在一起,有時候根本不能解決問題 |
|
|
|
#29樓 得分:0回複於:2008-11-18 09:27:49
引用 4 樓 Jim_King_2000 的回複: 兄弟,singleton是不用new的。如果用了new,在什麼地方去delete它呢?singleton原理的基礎是局部static變數。 C/C++ codeclassSingleton {private:staticSingleton*instance; Singleton() { }public:staticSingleton*GetInstance() {staticSingleton singleton;return&singleton } }; 這種局部static變數只有在函數被調用的第一次才被建立,隨著程式的結束而結束… private 保證只有一個實現, static data + static member function !=singleton, 不容易銷毀,那本書叫做 不死的鳳凰 |
|
|