c++單列模式與線程安全
通常c++裡面的單列模式很容易實現,我們也不需要去考慮其線程安全的問題,但是在多線程環境中我們卻必須要考慮到。首先我們來分析下一下的這個單列模式為什麼不是線程安全的,通常的單列模式寫法:
復制代碼
class MsgOfArrival
{
public:
~MsgOfArrival(void);
MAP_STRING_PNORMALMSGCOLLECTION m_normalmsgMap;
static MsgOfArrival* GetInstance();
private:
MsgOfArrival(void);
static MsgOfArrival* m_pInstance;
};
MsgOfArrival* MsgOfArrival::m_pInstance = NULL;
MsgOfArrival::MsgOfArrival(void)
{
}
MsgOfArrival::~MsgOfArrival(void)
{
}
復制代碼
假設兩個多線程同時執行GetInstance()函數,那麼m_pInstance==NULL時, 兩個線程同時進入了if中時就會創建兩個指針,這當然是不行的。解決這個問題首先我們會想到加鎖,如改成
復制代碼
MsgOfArrival* MsgOfArrival::GetInstance()
{
if (m_pInstance == NULL)
{
EnterCriticalSection(&g_cs);
m_pInstance = new MsgOfArrival;
LeaveCriticalSection(&g_cs)
}
return m_pInstance;
}
復制代碼
這樣當然可以,而且m_pInstance一但new成功,GetInstance調用不會在進入if中,所以大量調用該單列並不會因為臨界區造成性能瓶頸。
第二種方法是靜態變量初始化的時候,我們直接讓其new一個指針,因為我們知道靜態變量是在Main函數還沒執行的時候就由主線程完成了初始化,既然單例指針還沒進入到main就已經構造了, 那麼我們當然不需要在進入main以後的多線程中來擔心單例構造的線程安全性。代碼如下:
復制代碼
class MsgOfArrival
{
public:
~MsgOfArrival(void);
MAP_STRING_PNORMALMSGCOLLECTION m_normalmsgMap;
static const MsgOfArrival* GetInstance();
private:
MsgOfArrival(void);
static const MsgOfArrival* m_pInstance;
};
const MsgOfArrival* MsgOfArrival::m_pInstance = new MsgOfArrival;
MsgOfArrival::MsgOfArrival(void)
{
}
MsgOfArrival::~MsgOfArrival(void)
{
}
const MsgOfArrival* MsgOfArrival::GetInstance()
{
return m_pInstance;
}
復制代碼
需要注意m_pInstance使用const進行修飾的,其所指對象是const的也就是說我們不能修改這個單列對象,這也要求我們在調用改單列的函數時該函數也必須是const的如:
復制代碼
int getdata() const; //函數聲明
//函數定義
int MsgOfArrival::getdata() const
{
//
// return m_nVal;
}
復制代碼
當然m_pInstance也可以不用const修飾, 但是這樣其其內部數據可以被修改, 這樣在多線程的時候是很危險的。
上面單例模式已經是線程安全的, 但是有個問題, 這個單例被創建後如何釋放呢?第一種方法 在在類裡面增加一個靜態的函數用於釋放指針,這樣做在程序中我們隨時可以釋放單例指針。但是有時候我們的單例指針要一直伴隨著進程結束,這個時候我們就可以采用第二種方法:
復制代碼
class MsgOfArrival
{
public:
~MsgOfArrival(void);
MAP_STRING_PNORMALMSGCOLLECTION m_normalmsgMap;
static const MsgOfArrival* GetInstance();
private:
class CGarbo
{
public:
~CGarbo()
{
SAFE_DELETE(MsgOfArrival::m_pInstance);
}
};
static CGarbo Garbo; //清理資源
MsgOfArrival(void);
static const MsgOfArrival* m_pInstance;
};
復制代碼
我們在類裡面添加了一個CGarbo類,這個類只有一個作用負責清除單列對象指針,定義一個static的Garbo對象, 該對象聲明周期與單例對象指針是一樣的,這個靜態Garbo對象也會在程序結束時釋放,這個時候會將單例指針所擁有資源也釋放掉。注意SAFE_DELETE是我自己定義的釋放宏