C++11 中是否有任何方法可以为对象实现无锁缓存,并且可以安全地从多个线程访问该对象?我要缓存的计算不是 super 便宜,但也不是 super 昂贵,因此在我的情况下,需要锁定会破坏缓存的目的。 IIUC,std::atomic
不保证无锁。
编辑:由于计算并不是太昂贵,所以我实际上不介意它是否运行一次或两次太多。但我确实需要确保所有消费者都能获得正确的值(value)。在下面的简单示例中,无法保证这一点,因为由于内存重新排序,线程可能会获得未初始化的 m_val
值,因为另一个线程将 m_alreadyCalculated
设置为true,但尚未设置 m_val
的值。
Edit2:下面的注释指出,对于基本类型,std::atomic
可能是无锁的。如果是,下面示例中使用 C++11 的内存排序来确保 m_alreadyCalculated
不可能在 m_val
之前设置为 true 的正确方法是什么code> 的值设置了吗?
非线程安全缓存示例:
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()
不抛出异常。
关于c++ - C++11 中的无锁缓存实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36239880/