單例模式:
單例模式一般會分爲:餓漢單例和懶漢單例
餓漢式:
在類加載的時候就完成初始化,所以類加載比較慢,但是獲取對象的速度是比較快的.
懶漢式:
在類加載的時候不初始化,等到第一次被使用的時候才進行初始化.
餓漢式:
class Singlenton{
private:
Singlenton() = default;
~Singlenton() = default;
public:
static Singlenton & getInstance();
};
Singlenton & Singlenton::getInstance(){
static Singlenton c;
return c;
}
餓漢式
:
class Singlenton{
private:
Singlenton();
Singlenton(const Singlenton &);
public:
static Singlenton * getInstance();
static Singlenton* m_instance;
};
Singlenton * Singlenton::m_instance = nullptr;
Singlenton* Singlenton::getInstance() {
if(m_instance == nullptr){
m_instance = new Singlenton();
}
return m_instance;
}
這種方案對於多線程模式並不是安全的,有可能會同時創建多個對象
那麼,我們肯定會隨之進行加鎖操作,但是在這裏的話鎖的代價過高
Singlenton* Singlenton::getInstance() {
std::lock_guard<mutex> b(mutex_);
if(m_instance == nullptr){
m_instance = new Singlenton();
}
return m_instance;
}
線程A沒有釋放鎖,B無法進入,第一次之後,相當於多個線程僅僅是對這個地方進行讀取,而對於多線程讀的話是沒有必要進行加鎖,對於高併發的情況下,代價及其高昂
在這裏我們可以使用雙檢查鎖
Singlenton* Singlenton::getInstance() {
if(m_instance == nullptr){
//在執行這一行之前,有可能多個線程同時都執行到這一步
std::lock_guard<mutex> b(mutex_);
if(m_instance == nullptr){
m_instance = new Singlenton();
}
}
return m_instance;
}
//消除代價過高的問題,對於上一次的缺陷進行完善
但是仍然有問題,內存讀寫reorder可能會造成不安全的情況
線程是在指令層級進行時間片的搶奪,cpu執行指令可能並不如我們之前的預期情況執行
std::atomic<Singlenton*> Singlenton::m_instance;
Singlenton* Singlenton::getInstance() {
Singleton * temp = m_instance.load(std::memory_order_relaxed);
//只保證在同一個線程內,同一個原子變量的操作的執行序列不會被重排序(reorder),這種保證也稱之爲modification order consistency,但是其他線程看到的這些操作的執行序列式不同的。
std::atomic_thread_fence(std::memory_order_acquire); //獲取內存的fence獲取存儲
if(temp == nullptr){
//在執行這一行之前,有可能多個線程同時都執行到這一步
std::lock_guard<mutex> b(mutex_);
if(temp== nullptr){
temp = new Singlenton();
std::atomic_thread_fence(std::memory_order_release); //釋放內存fence
m_instance.store(temp,std::memory_order_relaxed);
}
}
return m_instance;
}