1. 程式人生 > >實現一個執行緒安全的單例模式

實現一個執行緒安全的單例模式

一、單例模式

       單例模式也叫單件模式。Singleton是一個非常常用的設計模式,幾乎所有稍微大一些的程式都會使用它,所以構建一個高效的Singleton很重要。

1、單例類保證全域性只有一個唯一例項物件

2、單例類提供獲取這個唯一例項的介面。

        我們都能想到一個簡單的單例模式該怎樣實現:建構函式宣告為private或protec防止被外部函式例項化,內部儲存一個private static的類指標儲存唯一的例項,例項的動作由一個public的類方法代勞,該方法也返回單例類唯一的例項。

先看一段程式碼

class Singleton
{
public:
	//獲取唯一物件例項的介面函式
	static Singleton* GetInstance()
	{
		if(_sInstance==NULL)
		{
			_sInstance=new Singleton();
		}
		return _sInstance;
	}
	//刪除示例物件
	static void DelInstance()
	{
		if(_sInstance)
		{
			delete _sInstance;
			_sInstance=NULL;
		}
	}
	void Print()
	{
		cout<<_data<<endl;
	}
private:
	//建構函式定義為私有,限制只能在類內建立物件
	Singleton()
		:_data(0)
	{}
	Singleton(const Singleton&);
	Singleton& operator=(const Singleton&);
	//指向例項的指標定義為靜態私有,這樣定義靜態成員函式獲取物件例項
	static Singleton* _sInstance;
	//單例類裡面的資料
	int _data;
}
void TestSingleton()
{
	TestSingleton::GetInstance()->Print();
	TestSingleton::DelInstance;
}
        這個方法簡單易懂,但是它是執行緒不安全的,如果兩個執行緒同時呼叫_sInstance方法且同時檢測到p是NULL值,則兩個執行緒會同時構造一個例項給p,這是嚴重的錯誤。當然我們也有執行緒安全的實現方法。

二、懶漢模式與餓漢模式
       單例大約有兩種實現方法:懶漢與餓漢。

       *懶漢:顧名思義,不到萬不得已就不回去例項化,也就是說在第一次用到類例項的時候才回去例項化。

       *餓漢:與懶漢相反,它是飢不擇食的,所以在單例類定義的時候就進行例項化。

        兩者的特點與選擇要點:

       *由於要進行執行緒同步,所以在訪問量較大,或者可能訪問的執行緒較多時採用餓漢實現,可以實現更好的效能。這是以空間換時間。

       *在訪問量較小時,可以實現懶漢模式。這是以時間換空間。

三、執行緒安全的的懶漢模式

       *方法一:加鎖的經典懶漢實現:

class singleton
{
protected:
	singleton()
	{
		pthread_mutex_init(&mutex);
	}
private:
	static singleton* p;
public:
	static pthread_mutex_t mutex;
	static singleton* instance();
};
pthread_mutex_t singleton::mutex;
singleton* singleton::p=NULL;
singleton* singleton::instance()
{
	if(p==NULL)
	{
		pthread_mutex_lock(&mutex);
		if(p==NULL)
		{
			p=new singleton();
		}
		pthread_mutex_unlock(&mutex);
	}
	return p;
}
       *方法二:內部靜態變數的懶漢實現

這個方法也比較簡單,在instance函式裡定義一個靜態的例項,也可以保證擁有唯一例項,在返回時只需要返回其指標就可以了。

class singleton
{
protected:
	singleton()
	{
		pthread_mutex_init(&mutex);
	}
public:
	static pthread_mutex_t mutex;
	static singleton* instance();
	int a;
};
pthread_mutex_t singleton::mutex;
singleton* singleton::instance()
{
	pthread_mutex_lock(&mutex);
	static singleton obj;
	pthread_mutex_unlock(&mutex);
	return &obj;
}

四、餓漢模式

        執行緒安全本來就是安全的,不用加鎖,因為在餓漢模式下,在單例類定義時就已經定義了一個物件,對類進行了初始化。後面不管哪個執行緒呼叫函式instance(),都只不過時返回一個物件的指標而已。所以是執行緒安全的,不需要在成員函式instance中加鎖。

class singleton
{
protected:
	singleton()
	{}
private:
	static singleton* p;
public:
	static singleton* initance();
	int a;
};
singleton* singleton::p=new singleton;
singleton* singleton::initance()
{
	return p;
}
int main()
{
	singleton* s=singleton::instance();
	s->a=10;
	return 0;
}