在某些應(yīng)用環(huán)境下面,一個(gè)類只允許有一個(gè)實(shí)例,這就是著名的單例模式。單例模式分為懶漢模式,跟餓漢模式兩種。
網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)!專注于網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、小程序制作、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了西安免費(fèi)建站歡迎大家使用!
首先給出餓漢模式的實(shí)現(xiàn)
正解:
template <class T> class singleton { protected: singleton(){}; private: singleton(const singleton&){};//禁止拷貝 singleton& operator=(const singleton&){};//禁止賦值 static T* m_instance; public: static T* GetInstance(); }; template <class T> T* singleton<T>::GetInstance() { return m_instance; } template <class T>
在實(shí)例化m_instance 變量時(shí),直接調(diào)用類的構(gòu)造函數(shù)。顧名思義,在還未使用變量時(shí),已經(jīng)對(duì)m_instance進(jìn)行賦值,就像很饑餓的感覺。這種模式,在多線程環(huán)境下肯定是線程安全的,因?yàn)椴淮嬖诙嗑€程實(shí)例化的問題。
下面來(lái)看懶漢模式
template <class T> class singleton { protected: singleton(){}; private: singleton(const singleton&){}; singleton& operator=(const singleton&){}; static T* m_instance; public: static T* GetInstance(); }; template <class T> T* singleton<T>::GetInstance() { if( m_instance == NULL) { m_instance = new T(); } return m_instance; } template <class T> T* singleton<T>::m_instance = NULL;
懶漢模式下,在定義m_instance變量時(shí)先等于NULL,在調(diào)用GetInstance()方法時(shí),在判斷是否要賦值。這種模式,并非是線程安全的,因?yàn)槎鄠€(gè)線程同時(shí)調(diào)用GetInstance()方法,就可能導(dǎo)致有產(chǎn)生多個(gè)實(shí)例。要實(shí)現(xiàn)線程安全,就必須加鎖。
下面給出改進(jìn)之后的代碼
template <class T>
class singleton
{
protected:
singleton(){};
private:
singleton(const singleton&){};
singleton& operator=(const singleton&){};
static T* m_instance;
static pthread_mutex_t mutex;
public:
static T* GetInstance();
};
template <class T>
T* singleton<T>::GetInstance()
{
pthread_mutex_lock(&mutex);
if( m_instance == NULL)
{
m_instance = new T();
}
pthread_mutex_unlock(&mutex);
return m_instance;
}
template <class T>
pthread_mutex_t singleton<T>::mutex = PTHREAD_MUTEX_INITIALIZER;
template <class T>
T* singleton<T>::m_instance = NULL;
這一切看起來(lái)都很完美,但是程序猿是一種天生就不知道滿足的動(dòng)物。他們發(fā)現(xiàn)GetInstance()方法,每次進(jìn)來(lái)都要加鎖,會(huì)影響效率。然而這并不是必須的,于是又對(duì)GetInstance()方法進(jìn)行改進(jìn)
template <class T> T* singleton<T>::GetInstance() { if( m_instance == NULL) { pthread_mutex_lock(&mutex); if( m_instance == NULL) { m_instance = new T(); } pthread_mutex_unlock(&mutex); } return m_instance; }
這也就是所謂的“雙檢鎖”機(jī)制。但是有人質(zhì)疑這種實(shí)現(xiàn)還是有問題,在執(zhí)行 m_instance = new T()時(shí),可能 類T還沒有初始化完成,m_instance 就已經(jīng)有值了。這樣會(huì)導(dǎo)致另外一個(gè)調(diào)用GetInstance()方法的線程,獲取到還未初始化完成的m_instance 指針,如果去使用它,會(huì)有意料不到的后果。其實(shí),解決方法也很簡(jiǎn)單,用一個(gè)局部變量過渡下即可:
正解:
template <class T> T* singleton<T>::GetInstance() { if( m_instance == NULL) { pthread_mutex_lock(&mutex); if( m_instance == NULL) { T* ptmp = new T(); m_instance = ptmp; } pthread_mutex_unlock(&mutex); } return m_instance; }
到這里在懶漢模式下,也就可以保證線程安全了。
然而,在linux下面還有另一種實(shí)現(xiàn)。linux提供了一個(gè)叫pthread_once()的函數(shù),它保證在一個(gè)進(jìn)程中,某個(gè)函數(shù)只被執(zhí)行一次。下面是使用pthread_once實(shí)現(xiàn)的線程安全的懶漢單例模式
template <class T> class singleton { protected: singleton(){}; private: singleton(const singleton&){}; singleton& operator=(const singleton&){}; static T* m_instance; static pthread_once_t m_once; public: static void Init(); static T* GetInstance(); }; template <class T> void singleton<T>::Init() { m_instance = new T(); } template <class T> T* singleton<T>::GetInstance() { pthread_once(&m_once,Init); return m_instance; } template <class T> pthread_once_t singleton<T>::m_once = PTHREAD_ONCE_INIT; template <class T> T* singleton<T>::m_instance = NULL;
上面的單例類使用了模板,對(duì)每一種類型的變量都能實(shí)例化出唯一的一個(gè)實(shí)例。
例如要實(shí)例化一個(gè)int類型
int *p = singleton<int>::GetInstance()
例如要實(shí)例化一個(gè)string類型
string *p = singleton<string>::GetInstance()
在上面的實(shí)現(xiàn)中,在實(shí)例化對(duì)象時(shí),調(diào)用GetInstance()函數(shù)時(shí)都沒有傳遞參數(shù),這是猶豫不同的對(duì)象其初始化時(shí)參數(shù)個(gè)數(shù)都不一樣。如果要支持不同類型的對(duì)象帶參數(shù)初始化,則需要重載GetInstance函數(shù)。然而在c++11中,已經(jīng)支持了可變參數(shù)函數(shù)。這里給出一個(gè)簡(jiǎn)單的例子
#ifndef _SINGLETON_H_ #define _SINGLETON_H_ template <class T> class singleton { protected: singleton(){}; private: singleton(const singleton&){}; singleton& operator=(const singleton&){}; static T* m_instance; public: template <typename... Args> static T* GetInstance(Args&&... args) { if(m_instance == NULL) m_instance = new T(std::forward<Args>(args)...); return m_instance; } static void DestroyInstance() { if(m_instance ) delete m_instance; m_instance = NULL; } }; template <class T> T* singleton<T>::m_instance = NULL; #endif
測(cè)試函數(shù)
#include <iostream> #include <string> #include "singleton.h" using namespace std; struct A { A(int a ,int b):_a(a),_b(b) {} int _a; int _b; }; int main() { int *p1 = singleton<int>::GetInstance(5); int *p2 = singleton<int>::GetInstance(10); cout << *p1 << " " << *p2 <<endl; string *p3 = singleton<string>::GetInstance("aa"); string *p4 = singleton<string>::GetInstance("bb"); cout << *p3 << " " << *p4 <<endl; A *p5 = singleton<A>::GetInstance(1,2); A *p6 = singleton<A>::GetInstance(4,5); cout << p5->_a << " " << p6->_a<<endl; return 0; }
運(yùn)行結(jié)果如下
以上所述是小編給大家介紹的C++實(shí)現(xiàn)線程安全的單例模式詳解整合,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)創(chuàng)新互聯(lián)網(wǎng)站的支持!
名稱欄目:詳解C++實(shí)現(xiàn)線程安全的單例模式
轉(zhuǎn)載來(lái)于:http://jinyejixie.com/article22/ijjdcc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供云服務(wù)器、網(wǎng)站改版、建站公司、、網(wǎng)站設(shè)計(jì)、品牌網(wǎng)站設(shè)計(jì)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)