實現一個執行緒安全的單例模式
阿新 • • 發佈:2019-02-04
一、單例模式
單例模式也叫單件模式。Singleton是一個非常常用的設計模式,幾乎所有稍微大一些的程式都會使用它,所以構建一個高效的Singleton很重要。
1、單例類保證全域性只有一個唯一例項物件
2、單例類提供獲取這個唯一例項的介面。
我們都能想到一個簡單的單例模式該怎樣實現:建構函式宣告為private或protec防止被外部函式例項化,內部儲存一個private static的類指標儲存唯一的例項,例項的動作由一個public的類方法代勞,該方法也返回單例類唯一的例項。
先看一段程式碼
這個方法簡單易懂,但是它是執行緒不安全的,如果兩個執行緒同時呼叫_sInstance方法且同時檢測到p是NULL值,則兩個執行緒會同時構造一個例項給p,這是嚴重的錯誤。當然我們也有執行緒安全的實現方法。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; }
二、懶漢模式與餓漢模式
單例大約有兩種實現方法:懶漢與餓漢。
*懶漢:顧名思義,不到萬不得已就不回去例項化,也就是說在第一次用到類例項的時候才回去例項化。
*餓漢:與懶漢相反,它是飢不擇食的,所以在單例類定義的時候就進行例項化。
兩者的特點與選擇要點:
*由於要進行執行緒同步,所以在訪問量較大,或者可能訪問的執行緒較多時採用餓漢實現,可以實現更好的效能。這是以空間換時間。
*在訪問量較小時,可以實現懶漢模式。這是以時間換空間。
三、執行緒安全的的懶漢模式
*方法一:加鎖的經典懶漢實現:
*方法二:內部靜態變數的懶漢實現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;
}