[英]fetch_sub with memory_order_relaxed for atomic reference counting?
std::atomic<int> cnt = {2};
thread 1:
doFoo();
if (cnt.fetch_sub(1, std::memory_order_relaxed) == 1) {
doBazz();
}
thread 2:
doBar();
if (cnt.fetch_sub(1, std::memory_order_relaxed) == 1) {
doBazz();
}
如果線程 2<\/strong>調用
doBazz()<\/code>可以保證
doBazz()<\/code>看到
doBar()<\/code> () 的所有副作用,因為它們在同一個線程上運行並且它們之間存在先序<\/em>關系。
但不能保證它看到
doFoo()<\/code>的副作用或
doFoo()<\/code>已被調用。
這是因為
doFoo()<\/code>和
doBazz()<\/code>之間沒有形成之前發生的<\/em>關系。
如果線程 1<\/strong>使用
std::memory_order_release<\/code>減少
cnt<\/code>並且線程 2<\/strong>在訪問
cnt<\/code>時使用
std::memory_order_acquire<\/code> ,則會形成這種關系,這將在它們之間創建一個同步點。
線程 1<\/strong>調用
doBazz()<\/code>的情況是對稱的。
因此,在兩個線程中使用
cnt.fetch_sub(1, std::memory_order_acq_rel)<\/code> (
std::memory_order_acq_rel<\/code>結合了
std::memory_order_acquire<\/code>和
std::memory_order_release<\/code> )將提供您所詢問的保證。
我們也可以使用
cnt.fetch_sub(1, std::memory_order_release)<\/code>並在調用
doBazz()<\/code>之前調用
std::atomic_thread_fence(std::memory_order_acquire)<\/code> ) 來實現相同的目的。
沒有記憶排序,還有你的代碼在所有,這樣保證不成立。 但是,可以通過使fetch_sub
成為發布序列的一部分來使用放松的順序並使其正常工作:
std::atomic<int> cnt{0};
cnt.store(2, std::memory_order_release); // initiate release sequence (RS)
//thread 1:
doFoo();
if (cnt.fetch_sub(1, std::memory_order_relaxed) == 1) { // continue RS
std::atomic_thread_fence(std::memory_order_acquire); // synchronizes with RS
doBazz();
}
//thread 2:
doBar();
if (cnt.fetch_sub(1, std::memory_order_relaxed) == 1) { // continue RS
std::atomic_thread_fence(std::memory_order_acquire); // synchronizes with RS
doBazz();
}
要么
void doBazz();
std::atomic<int> cnt{0};
cnt.store(2, std::memory_order_release); // initiate release sequence (RS)
//thread 1:
doFoo();
if (cnt.fetch_sub(1, std::memory_order_relaxed) == 1) { // continue RS
doBazz();
}
//thread 2:
doBar();
if (cnt.fetch_sub(1, std::memory_order_relaxed) == 1) { // continue RS
doBazz();
}
void doBazz() {
std::atomic_thread_fence(std::memory_order_acquire); // synchronizes with RS
// ...
}
這些保證了doFoo()
和doBar()
總是在doBazz()
之前發生。
http://en.cppreference.com/w/cpp/atomic/memory_order
即使使用寬松的內存模型,也不允許空中值循環地依賴於它們自己的計算,例如x和y最初為零,
// Thread 1:
r1 = x.load(memory_order_relaxed);
if (r1 == 42) y.store(r1, memory_order_relaxed);
// Thread 2:
r2 = y.load(memory_order_relaxed);
if (r2 == 42) x.store(42, memory_order_relaxed);
不允許產生r1 == r2 == 42,因為只有到x存儲42的存儲才有可能存儲到y的42,這在循環上取決於到y存儲42的存儲。請注意,直到C ++ 14,這規范在技術上是允許的,但不建議實現者使用。
即使使用了memory_order_relaxed,仍然存在一些不允許執行的命令。 在我看來
cnt.fetch_sub(1, std::memory_order_relaxed) == 2
應該在之前發生
cnt.fetch_sub(1, std::memory_order_relaxed) == 1
對? 因此,doFoo()和doBar()都應發生在doBazz()之前。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.