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