單例模式全面學習(C++版)
單例模式:用來創建獨一無二的,只能夠有一個實例的對象。 單例模式的結構是設計模式中最簡單的,但是想要完全實現一個線程安全的單例模式還是有很多陷阱的,所以面試的時候屬於一個常見的考點~
單例模式的應用場景:有一些對象其實只需要一個,比如:線程池,緩存,對話框,處理偏好設置和注冊表的對象,日志對象,充當打印機,顯卡等設備的驅動程序對象。這些對象只能夠擁有一個實例,如果創建出了多個實例,就會導致一些程序的問題。程序的行為異常,資源使用的過量,或者導致不一致的結果。常用來管理共享的資源,比如數據庫的連接或者線程池。
單例模式的類圖非常簡單,如下~,並且經典的實現也非常的簡單。
class Singleton {
public:
static Singleton* getInstance();
//析構的時候釋放資源~
~Singleton() {
if( (_instance != NULL)
delete _instance;
}
protected:
Singleton();
private:
static Singleton* _instance;
}
Singleton *Singleton::_instance = NULL;
Singleton* Singleton::getInstance() {
if( _instance == NULL) {
_instance = new Singleton();
}
return _instance;
}
經典的實現非常容易,但是存在一個問題,就是這個經典的實現非線程安全,多線程的情況下,這個單例模式的實現會出現問題~,如何解決呢?改進!
class Lock
{
private:
mutex mtex;
public:
Lock(mutex m) : mtex(m)
{
mtex.Lock();
}
~Lock()
{
mtex.Unlock();
}
};
class Singleton {
public:
static Singleton* getInstance();
//析構的時候釋放資源~
~Singleton() {
if( (_instance != NULL)
delete _instance;
}
protected:
Singleton();
private:
static Singleton* _instance;<br> static mutex m;
}
Singleton *Singleton::_instance = NULL;
Singleton* Singleton::getInstance() {<br> //check 之前進行臨界區加鎖操作<br> Lock lock(m);
if( _instance == NULL) {
_instance = new Singleton();
}
return _instance;
}
線程安全保證的一種方法及為在check _instance == NULL 之前進行臨界區加鎖,如果已經有一個線程進入訪問,其他線程必須等待,這樣就能夠保證多線程情況下實例的唯一!
but,互斥的同步會導致性能的降低,即使_instance已經不為空了,每次還是需要加鎖,這樣操作花費就比較多,性能必定比較差。
另外還有一些比較好的方法:
1.(非線程同步的方法)上面的操作均為一種lazy initialization的思想,及用到的時候在初始化,這樣程序效率比較高,但是有一個另外比較好的方法可以采用是提前初始化,將_instance設置為static之後直接初始化為Singleton對象,每次只需要執行返回操作即可。
這樣的話同樣會導致問題,就是如果單例本來資源比較多,但是不需要創建那麼早,就會消耗資源~。
class Singleton {
public:
static Singleton* getInstance();
//析構的時候釋放資源~
~Singleton() {
delete _instance;
}
protected:
Singleton();
private:
static Singleton* _instance;
}
Singleton *Singleton::_instance = new Singleton();
Singleton* Singleton::getInstance() {
return _instance;
}
2.另外一種提升因為同步導致的性能變差的方法稱為“雙重檢驗加鎖”。方法如下:
思路是只有在第一次創建的時候進行加鎖,當_instance不為空的時候就不需要進行加鎖的操作,這樣就可以提升性能~
class Lock
{
private:
mutex mtex;
public:
Lock(mutex m) : mtex(m)
{
mtex.Lock();
}
~Lock()
{
mtex.Unlock();
}
};
class Singleton {
public:
static Singleton* getInstance();
//析構的時候釋放資源~
~Singleton() {
if( (_instance != NULL)
delete _instance;
}
protected:
Singleton();
private:
static Singleton* _instance;
static mutex m;
}
Singleton *Singleton::_instance = NULL;
Singleton* Singleton::getInstance() {
//check 之前進行臨界區加鎖操作
//雙重檢驗加鎖
if(_instance == NULL ) {
Lock lock(m);
if( _instance == NULL) {
_instance = new Singleton();
}
}
return _instance;
}
總之,小小單例模式問題還是挺多的,面試官喜歡問的一個問題~,因為還是有很多陷阱的。