1、簡單的單例模式實現

2、C++的構造函數不是線程安全的,所以上述代碼在多線程的情況下是不安全的,原因是new Singelton時,這句話不是原子的,比如一個線程執行了new的同時,另一個線程對if進行判斷(此時實例還沒被創建出來)。在windows下模擬:
#include <iostream>
#include <process.h>
#include <windows.h>
using namespace std;
class Singelton{
private:
Singelton(){
m_count ++;
printf("Singelton begin\n");
Sleep(1000); // 加sleep為了放大效果
printf("Singelton end\n");
}
static Singelton *single;
public:
static Singelton *GetSingelton();
static void print();
static int m_count;
};
Singelton *Singelton::single = nullptr;
int Singelton::m_count = 0;
Singelton *Singelton::GetSingelton(){
if(single == nullptr){
single = new Singelton;
}
return single;
}
void Singelton::print(){
cout<<m_count<<endl;
}
// 回調函數
void threadFunc(void *p){
DWORD id = GetCurrentThreadId(); // 獲得線程id
cout<<id<<endl;
Singelton::GetSingelton()->print(); // 構造函數並獲得實例,調用靜態成員函數
}
int main(int argc, const char * argv[]) {
int threadNum = 3;
HANDLE threadHdl[100];
// 創建3個線程
for(int i = 0; i<threadNum; i++){
threadHdl[i] = (HANDLE)_beginthread(threadFunc, 0, nullptr);
}
// 讓主進程等待所有的線程結束后再退出
for(int i = 0; i<threadNum; i++){
WaitForSingleObject(threadHdl[i], INFINITE);
}
cout<<"main"<<endl; // 驗證主進程是否是最后退出
return 0;
}
運行結果:

該單例模式也稱為懶漢式單例。
懶漢:故名思義,不到萬不得已就不會去實例化類,也就是說在第一次用到類實例的時候才會去實例化。與之對應的是餓漢式單例。(注意,懶漢本身是線程不安全的,如上例子)
餓漢:餓了肯定要飢不擇食。所以在單例類定義的時候就進行實例化。(本身就是線程安全的,如下例子)
關於如何選擇懶漢和餓漢模式:
特點與選擇:
懶漢:在訪問量較小時,采用懶漢實現。這是以時間換空間。
餓漢:由於要進行線程同步,所以在訪問量比較大,或者可能訪問的線程比較多時,采用餓漢實現,可以實現更好的性能。這是以空間換時間。
3、餓漢式的單例實現
#include <iostream>
#include <process.h>
#include <windows.h>
using namespace std;
class Singelton{
private:
Singelton(){
m_count ++;
printf("Singelton begin\n");
Sleep(1000); // 加sleep為了放大效果
printf("Singelton end\n");
}
static Singelton *single;
public:
static Singelton *GetSingelton();
static void print();
static int m_count;
};
// 餓漢模式的關鍵:初始化即實例化
Singelton *Singelton::single = new Singelton;
int Singelton::m_count = 0;
Singelton *Singelton::GetSingelton(){
// 不再需要進行實例化
//if(single == nullptr){
// single = new Singelton;
//}
return single;
}
void Singelton::print(){
cout<<m_count<<endl;
}
// 回調函數
void threadFunc(void *p){
DWORD id = GetCurrentThreadId(); // 獲得線程id
cout<<id<<endl;
Singelton::GetSingelton()->print(); // 構造函數並獲得實例,調用靜態成員函數
}
int main(int argc, const char * argv[]) {
int threadNum = 3;
HANDLE threadHdl[100];
// 創建3個線程
for(int i = 0; i<threadNum; i++){
threadHdl[i] = (HANDLE)_beginthread(threadFunc, 0, nullptr);
}
// 讓主進程等待所有的線程結束后再退出
for(int i = 0; i<threadNum; i++){
WaitForSingleObject(threadHdl[i], INFINITE);
}
cout<<"main"<<endl; // 驗證主進程是否是最后退出
return 0;
}
運行結果:

4、線程安全的懶漢式單例的實現
餓漢式會提前浪費我們的內存空間以及資源,如果有項目中要求我們在使用到實例的時候再去實例化,則還是需要使用懶漢式。
class singleton
{
protected:
singleton()
{
// 初始化
pthread_mutex_init(&mutex);
}
private:
static singleton* p;
public:
static pthread_mutex_t mutex;
static singleton* initance();
};
pthread_mutex_t singleton::mutex;
singleton* singleton::p = NULL;
singleton* singleton::initance()
{
if (p == NULL)
{
// 加鎖
pthread_mutex_lock(&mutex);
if (p == NULL)
p = new singleton();
pthread_mutex_unlock(&mutex);
}
return p;
}
需要注意的是:上面進行的兩次if(p == NULL)的檢查,因為當獲得了實例之后,有了外層的判斷之后,就不會再進入到內層判斷,即不會再進行lock以及unlock的操作。
