[英]Is implementation of double checked singleton thread-safe?
我知道線程安全單例的常見實現如下所示:
Singleton* Singleton::instance() {
if (pInstance == 0) {
Lock lock;
if (pInstance == 0) {
Singleton* temp = new Singleton; // initialize to temp
pInstance = temp; // assign temp to pInstance
}
}
return pInstance;
}
但是為什么他們說這是一個線程安全的實現呢?
例如,第一個線程可以在pInstance == 0
上傳遞兩個測試,創建new Singleton
並將其分配給temp
指針,然后開始賦值pInstance = temp
(據我所知,指針賦值操作不是原子的)。
同時第二個線程測試第一個pInstance == 0
,其中pInstance
只分配了一半。 它不是nullptr,但也不是有效的指針,然后從函數返回。 這樣的情況會發生嗎? 我沒有在任何地方找到答案,似乎這是一個非常正確的實現,我什么都不懂
它不是由C ++並發規則安全的,因為第一讀pInstance
沒有被鎖或類似的保護,因此不會寫入(這是受保護的)正確同步。 因此存在數據競爭,因此存在未定義的行為。 這個UB的一個可能的結果正是你已經確定的:第一次檢查讀取pInstance
的垃圾值,這是由不同的線程寫的。
常見的解釋是,在更常見的情況下( pInstance
已經有效),它可以節省獲取鎖(一個可能耗時的操作)。 但是,這不安全。
由於C ++ 11及更高版本保證了函數范圍的初始化,靜態變量只發生一次並且是線程安全的,因此在C ++中創建單例的最佳方法是在函數中使用靜態局部:
Singleton& Singleton::instance() {
static Singleton s;
return s;
}
請注意,不需要動態分配或指針返回類型。
正如Voo在評論中提到的,上面假設pInstance
是一個原始指針。 如果它是std::atomic<Singleton*>
,那么代碼可以正常工作。 當然,這是一個問題,原子讀取是否比獲取鎖定慢得多,這應該通過分析來回答。 盡管如此,這將是一個相當無意義的練習,因為靜態局部變量在所有但非常模糊的情況下都更好。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.