C++ 11中有什么方法可以为对象实现无锁缓存,从多个线程可以安全地访问它吗?我要缓存的计算并不是很便宜,但也不是太昂贵,因此在我的情况下,需要使用锁才能达到缓存的目的。 IIUC,不能保证std::atomic是无锁的。

编辑:由于计算不是太昂贵,我实际上不介意它运行一次或两次太多。但是,我需要确保所有消费者都获得正确的值(value)。在下面的幼稚示例中,这不能保证,因为由于内存重新排序,一个线程可能会获得未初始化的m_val值,因为另一个线程将m_alreadyCalculated设置为true,但尚未设置m_val的值。

Edit2:下面的注释指出,对于基本类型,std::atomic可能是无锁的。如果是这样,在下面的示例中使用C++ 11的内存排序以确保在设置m_alreadyCalculated的值之前不可能将m_val设置为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()不引发异常。

07-24 09:48
查看更多