詳解如何使用C++寫一個線程安全的單例模式_第1頁
詳解如何使用C++寫一個線程安全的單例模式_第2頁
詳解如何使用C++寫一個線程安全的單例模式_第3頁
詳解如何使用C++寫一個線程安全的單例模式_第4頁
詳解如何使用C++寫一個線程安全的單例模式_第5頁
已閱讀5頁,還剩2頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

第詳解如何使用C++寫一個線程安全的單例模式目錄單例模式的簡單實現(xiàn)有問題的雙重檢測鎖現(xiàn)代C++中的解決方法使用現(xiàn)代C++中的內(nèi)存順序限制使用現(xiàn)代C++中的call_once方法使用靜態(tài)局部變量

單例模式的簡單實現(xiàn)

單例模式大概是流傳最為廣泛的設(shè)計模式之一了。一份簡單的實現(xiàn)代碼大概是下面這個樣子的:

classsingleton

public:

staticsingleton*instance()

if(inst_!=nullptr){

inst_=newsingleton();

returninst_;

private:

singleton(){}

staticsingleton*inst_;

singleton*singleton::inst_=nullptr;

這份代碼在單線程的環(huán)境下是完全沒有問題的,但到了多線程的世界里,情況就有一點不同了??紤]以下執(zhí)行順序:

線程1執(zhí)行完if(inst_!=nullptr)之后,掛起了;線程2執(zhí)行instance函數(shù):由于inst_還未被賦值,程序會inst_=newsingleton()語句;線程1恢復(fù),inst_=newsingleton()語句再次被執(zhí)行,單例句柄被多次創(chuàng)建。

所以,這樣的實現(xiàn)是線程不安全的。

有問題的雙重檢測鎖

解決多線程的問題,最常用的方法就是加鎖唄。于是很容易就可以得到以下的實現(xiàn)版本:

classsingleton

public:

staticsingleton*instance()

guardmutexlock{mut_};

if(inst_!=nullptr){

inst_=newsingleton();

returninst_;

private:

singleton(){}

staticsingleton*inst_;

staticmutexmut_;

singleton*singleton::inst_=nullptr;

mutexsingleton::mut_;

這樣問題是解決了,但性能上就不那么另人滿意,畢竟每一次使用instance都多了一次加鎖和解鎖的開銷。更關(guān)鍵的是,這個鎖也不是每次都需要??!實際我們只有在創(chuàng)建單例實例的時候才需要加鎖,之后使用的時候是完全不需要鎖的。于是,有人提出了一種雙重檢測鎖的寫法:

...

staticsingleton*instance()

if(inst_!=nullptr){

guardmutexlock{mut_};

if(inst_!=nullptr){

inst_=newsingleton();

returninst_;

我們先判斷一下inst_是否已經(jīng)初始化了,如果沒有,再進行加鎖初始化流程。這樣,雖然代碼看上去有點怪異,但好像確實達到了只在創(chuàng)建單例時才引入鎖開銷的目的。不過遺憾的是,這個方法是有問題的。ScottMeyers和AndreiAlexandrescu兩位大神在C++andthePerilsofDouble-CheckedLocking一文中對這個問題進行了非常詳細地討論,我們在這兒只作一個簡單的說明,問題出在:

inst_=newsingleton();

這一行。這句代碼不是原子的,它通常分為以下三步:

調(diào)用operatornew為singleton對象分配內(nèi)存空間;在分配好的內(nèi)存空間上調(diào)用singleton的構(gòu)造函數(shù);將分配的內(nèi)存空間地址賦值給inst_。

如果程序能嚴格按照1--2--3的步驟執(zhí)行代碼,那么上述方法沒有問題,但實際情況并非如此。編譯器對指令的優(yōu)化重排、CPU指令的亂序執(zhí)行(具體示例可參考《【多線程那些事兒】多線程的執(zhí)行順序如你預(yù)期嗎?》)都有可能使步驟3執(zhí)行早于步驟2??紤]以下的執(zhí)行順序:

線程1按步驟1--3--2的順序執(zhí)行,且在執(zhí)行完步驟1,3之后被掛起了;線程2執(zhí)行instance函數(shù)獲取單例句柄,進行進一步操作。

由于inst_在線程1中已經(jīng)被賦值,所以在線程2中可以獲取到一個非空的inst_實例,并繼續(xù)進行操作。但實際上單例對像的創(chuàng)建還沒有完成,此時進行任何的操作都是未定義的。

現(xiàn)代C++中的解決方法

在現(xiàn)代C++中,我們可以通過以下幾種方法來實現(xiàn)一個即線程安全、又高效的單例模式。

使用現(xiàn)代C++中的內(nèi)存順序限制

現(xiàn)代C++規(guī)定了6種內(nèi)存執(zhí)行順序。合理的利用內(nèi)存順序限制,即可避免代碼指令重排。一個可行的實現(xiàn)如下:

classsingleton{

public:

staticsingleton*instance()

singleton*ptr=inst_.load(memory_order_acquire);

if(ptr==nullptr){

lock_guardmutexlock{mut_};

ptr=inst_.load(memory_order_relaxed);

if(ptr==nullptr){

ptr=newsingleton();

inst_.store(ptr,memory_order_release);

returninst_;

private:

singleton(){};

staticmutexmut_;

staticatomicsingleton*inst_;

mutexsingleton::mut_;

atomicsingleton*singleton::inst_;

來看一下匯編代碼:

可以看到,編譯器幫我們插入了必要的語句來保證指令的執(zhí)行順序。

使用現(xiàn)代C++中的call_once方法

call_once也是現(xiàn)代C++中引入的新特性,它可以保證某個函數(shù)只被執(zhí)行一次。使用call_once的代碼實現(xiàn)如下:

classsingleton

public:

staticsingleton*instance()

if(inst_!=nullptr){

call_once(flag_,create_instance);

returninst_;

private:

singleton(){}

staticvoidcreate_instance()

inst_=newsingleton();

staticsingleton*inst_;

staticonce_flagflag_;

singleton*singleton::inst_=nullptr;

once_flagsingleton::flag_;

來看一下匯編代碼:

可以看到,程序最終調(diào)用了__gthrw_pthread_once來保證函數(shù)只被執(zhí)行一次。

使用靜態(tài)局部變量

現(xiàn)在C++對變量的初始化順序有如下規(guī)定:

Ifcontrolentersthedeclarationconcurrentlywhilethevariableisbeinginitialized,theconcurrentexecutionshallwaitforcompletionoftheinitialization.

所以我們可以簡單的使用一個靜態(tài)局部變量來實現(xiàn)線程安全的單例模式:

classsingleton

public:

staticsingleton*instance()

staticsingletoninst_

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論