簡體   English   中英

"fetch_sub 和 memory_order_relaxed 用於原子引用計數?"

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

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