簡體   English   中英

是雙重檢查單例線程安全的實現?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM