簡體   English   中英

C++11 中的無鎖緩存實現

[英]Lock-free cache implementation in C++11

在 C++11 中有什么方法可以為對象實現無鎖緩存,從多個線程訪問它是安全的嗎? 我要緩存的計算不是超級便宜,但也不是超級昂貴,因此在我的情況下需要鎖會破壞緩存的目的。 IIUC, std::atomic不能保證是無鎖的。

編輯:由於計算不是太昂貴,我實際上不介意它運行一次或兩次太多。 但我 - 確實 - 需要確保所有消費者獲得正確的價值。 在下面的簡單示例中,這不能保證,因為由於內存重新排序,線程可能會獲得m_val的未初始化值,因為另一個線程將m_alreadyCalculated設置為 true,但尚未設置m_val的值。

Edit2:下面的評論指出,對於基本類型, std::atomic可能是無鎖的。 如果是這樣,在下面的示例中,使用 C++11 的內存排序來確保在設置m_val的值之前不可能將m_alreadyCalculated設置為 true 的正確方法是什么?

非線程安全緩存示例:

class C {
public:
   C(int param) : m_param(param) {}

   getValue() {
      if (!m_alreadyCalculated) {
          m_val = calculate(m_param);
          m_alreadyCalculated = true;
      }
      return m_val;
   }

   double calculate(int param) {
       // Some calculation
   }

private:
   int m_param;
   double m_val;
   bool m_alreadyCalculated = false;
}

考慮如下:

class C {
public:
   double getValue() {
      if (alreadyCalculated == true)
         return m_val;

      bool expected = false;
      if (calculationInProgress.compare_exchange_strong(expected, true)) {
         m_val = calculate(m_param);
         alreadyCalculated = true;
      // calculationInProgress = false;
      }
      else {
     //  while (calculationInProgress == true)
         while (alreadyCalculated == false)
            ; // spin
      }
      return m_val;
   }

private:
   double m_val;
   std::atomic<bool> alreadyCalculated {false};
   std::atomic<bool> calculationInProgress {false};
};

它實際上不是無鎖的,里面有一個自旋鎖。 但是我認為如果您不想通過多個線程運行calculate() ,則無法避免這種鎖定。

getValue()在這里變得更復雜,但重要的是一旦計算出m_val ,它總是會在第一個if語句中立即返回。

更新

出於性能原因,將整個類填充到緩存行大小也可能是一個好主意。

更新 2

原始答案中有一個錯誤,感謝 JVApen 指出這一點(它由評論標記)。 變量calculationInProgress最好重命名為calculationHasStarted

另外,請注意,此解決方案假定calculate()不會引發異常。

std::atomic 不能保證是無鎖的,但您可以檢查std::atomic<T>::is_lock_free()std::atomic::is_always_lock_free()以查看您的實現是否可以無鎖。

另一種方法可能是使用std::call_once ,但是根據我的理解,這更糟糕,因為它旨在阻止其他線程。

因此,在這種情況下,對於 m_val 和 alreadyCalculated,我都會使用 std::atomic。 其中包含 2 個(或更多)線程計算相同結果的風險。

只是在這里回答一個技術問題:為了確保值在標志之前更新,您可以使用發布語義更新標志。 發布語義的含義是此更新必須(被視為)發生在所有先前的更新之后。 在 x86 上,它僅意味着更新前的編譯器障礙,並更新內存,而不是注冊,如下所示:

asm volatile("":::"memory");
*(volatile bool*)&m_alreadyCalculated = true;

這正是原子集在發布語義中所做的

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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