簡(jiǎn)介
單例模式是很常用的一種設(shè)計(jì)模式,在實(shí)現(xiàn)過(guò)程中要非常注意線程安全蝶俱,
我們會(huì)介紹四種方式來(lái)實(shí)現(xiàn)線程安全的單例模式:
- std::mutex實(shí)現(xiàn)單例模式
- std::call_once實(shí)現(xiàn)單例模式
- 借助static member實(shí)現(xiàn)單例模式
- 使用std::atomic來(lái)實(shí)現(xiàn)double check 模式
本文會(huì)介紹前兩種實(shí)現(xiàn)方法驼卖,后面兩種實(shí)現(xiàn)方法請(qǐng)參考本文.
實(shí)現(xiàn)方法
借助std::mutex
class SingleTon {
public:
static SingleTon* getInstance(void)
{
std::lock_guard<std::mutex> lk(s_mtx);
if (s_p == nullptr) {
s_p = new SingleTon();
}
return s_p;
}
public:
int getValue() {
return m_int;
}
private:
static std::mutex s_mtx;
static SingleTon* s_p;
SingleTon(SingleTon&) = delete;
SingleTon& operator=(SingleTon&) = delete;
SingleTon(SingleTon&&) = delete;
SingleTon& operator=(SingleTon&&) = delete;
SingleTon() {
m_int = 0;
}
private:
int m_int;
};
上面的實(shí)現(xiàn)中仲翎,可以保證在多線程環(huán)境下調(diào)用getInstance
是安全的烹植,但在每次調(diào)用該函數(shù)的時(shí)候叠纷,都要求獲取mutex
,
這會(huì)降低該函數(shù)的性能肮蛹。
基于以上的代碼勺择,我們?cè)趤?lái)做一些修改,看一下其中會(huì)存在什么樣的問(wèn)題伦忠。
static SingleTon* getInstance(void)
{
if(s_p == nullptr){
std::lock_guard<std::mutex> lk(s_mtx);
if(s_p == nullptr){
s_p = new SingleTon();
}
}
return s_p;
}
在解釋上面的修改之前省核,我們先記住兩個(gè)概念,編譯器的指令優(yōu)化會(huì)將部分指令打亂昆码,其次
是CPU也會(huì)亂序執(zhí)行气忠,這使得很多指令并不像我們想象中的順序那樣運(yùn)行。這使得上面的代碼
不是線程安全的.
s_p = new SingleTon();
這句代碼包括三個(gè)部分:
1. 申請(qǐng)內(nèi)存空間
2. 調(diào)用構(gòu)造函數(shù)初始化內(nèi)存空間
3. 將地址指針賦值給`s_p`
在實(shí)際運(yùn)行過(guò)程中赋咽,編譯器或CPU會(huì)執(zhí)行某些優(yōu)化旧噪,很有可能使得指令運(yùn)行順序變成1,3,2,
如果在執(zhí)行完1,3
之后,線程被切換脓匿,這時(shí)候問(wèn)題出現(xiàn)了淘钟,其他線程很有可能獲得該指針,但該指針?biāo)赶虻牡刂房臻g并沒(méi)有來(lái)得及進(jìn)行初始化亦镶。
借助std::call_once與std::once_flag
在c++中可以提供一個(gè)可調(diào)用對(duì)象(fn)給std::call_once
,該函數(shù)
可以確保fn只被調(diào)用一次日月。
#include <iostream>
#include <string>
//使用c++提供的std::call_once實(shí)現(xiàn)單例模式
class SingleTon {
public:
static SingleTon* getInstance(void)
{
std::call_once(s_flag,initSingleTon);
return s_p;
}
public:
int getValue() {
return m_int;
}
private:
static SingleTon* s_p;
static std::once_flag s_flag;
static void initSingleTon() {
s_p = new SingleTon();
}
SingleTon(SingleTon&) = delete;
SingleTon& operator=(SingleTon&) = delete;
SingleTon(SingleTon&&) = delete;
SingleTon& operator=(SingleTon&&) = delete;
SingleTon() {
m_int = 0;
}
private:
int m_int;
};
SingleTon *SingleTon::s_p = nullptr;
std::once_flag SingleTon::s_flag;
//測(cè)試代碼
int main()
{
auto obj = SingleTon::getInstance();
std::cout << obj->getValue() << std::endl;
}