保護共享資料的初始化過程
在多執行緒程式設計中,互斥量是最通用的保護共享資料的機制。但是在某些情況下,一些資源僅需要在第一次初始化的時候需要保護,其時候就可以不需要互斥變數的保護了。比如編碼中最常見的單例模式,核心**如下:
//(3)獲得本類例項的唯一全域性訪問點
static csinglton* getinstance()
//例項已經存在,直接該例項物件
return pinstance;
}
以上**在多執行緒環境中是不安全的,有可能導致建立物件兩次,導致記憶體洩漏;如果每次訪問之前都是加鎖,將大大影響訪問效能,即使能解決問題。
為了滿足多執行緒環境中高效和安全的特性,人們提出了雙重鎖定方式單例模式,**如下:
static csinglton* getinstance()
}//例項已經存在,直接該例項物件
return pinstance;
}//執行**
getinstance()->dosomething();//步驟二
但是在實踐中可以發現,會因為指令編排方式不同,導致一些異常情況發生。
因為執行緒a中pinstance分配了指標,csinglton的建構函式還沒有執行,但此時執行緒b開始呼叫getinstance()介面,因為pinstance已經非空,直接執行dosomething(),就導致異常情況發生了。
同樣的,建立靜態區域性變數的**,在c++11之前也存在搶著建立變數的問題:
class my_class;
my_class& get_my_class_instance()
std::call_once保護共享資料
為了解決雙重鎖定以及建立多個例項的問題,c++11標準庫提供了std::once_flag和std::call_once來處理只初始化一次的情況。使用std::call_once比顯式使用互斥量消耗的資源更少,並且還是無鎖式程式設計,減少了死鎖問題的發生。
call_once和once_flag的用法:
std::once_flag flag;
void ******_do_once());}
int _tmain(int argc, _tchar* ar**)
執行結果:
****** example: called once//只有乙個列印,目標函式僅被執行一次
main thread end
如何用std::call_once建立安全性的單例模式參見這篇文章call_once 使用方法
發現問題
在正常情況下,若在執行期間call_once呼叫的函式丟擲異常,則once_flag狀態不會翻轉,其他執行緒還可以繼續執行call_once;但是在vs2013測試發現,異常無法安裝預期進行,不知道是何種原因,這裡做個記錄。
測試**:
std::once_flag flag;
void may_throw_function(bool do_throw)
std::cout << "didn't throw, call_once will not attempt again\n"; // 保證一次
}void do_once(bool do_throw)
catch (...)
}int main(int argc, _tchar* ar**)
執行結果:
vs2013編譯的程式無法繼續執行,執行緒卡住,如下
td::call_once的替代方案
在前面的文章中提到,我們也可以使用靜態區域性變數的方式建立唯一例項,只是在多執行緒環境中,存在這麼一種情況:每個執行緒都認為他們是第乙個初始化這個變數,導致這個變數被建立兩次。
但在c++11標準中,這些問題都被解決了:初始化及定義完全在乙個執行緒中發生,並且沒有其他執行緒可在初始化完成前對其進行處理,條件競爭終止於初始化階段。當然,這個要求編譯要支援c++11才可以。
std::call_once的替代方案
class my_class;
my_class& get_my_class_instance()
本文**: 保護共享資料的方法
1.關中斷 2.使用訊號量 3.禁止任務切換 關中斷是最激烈的方法。它會影響系統中所有中斷程式和其他任務的響應時間。如果關中斷則也禁止了任務切換,因為排程程式不能控制處理器切換。關中斷有兩個優點。1.它是在資料由任務 和中斷程式共享的情況下,唯一能工作的方法。因為中斷程式,不允許獲取訊號量。而禁止任...
C 11多執行緒訪問時候的資料保護例項
1 include2 include3 include 4 include5 include6 include7 include8 using namespace std 910 用成員函式作為執行緒函式的方法寫執行緒 11 std cout 是共享物件 輸出流 應該確保它不受多個執行緒的同時訪問 ...
C 學習之共享資料的保護
對於既需要共享 又需要防止改變的資料應該宣告為常型別 用const進行修飾 對於不改變物件狀態的成員函式應該宣告為常函式。1 常物件 必須進行初始化,不能被更新。const 類名 物件名 2 常成員 用const進行修飾的類成員 常資料成員和常函式成員 3 常引用 被引用的物件不能被更新。const...