带有memory_order_relaxed的fetch_sub用于原子引用计数?

时间:2016-07-19 02:59:02

标签: c++ multithreading c++11 atomic

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();
    }

我们可以保证doFoo()doBar()始终在doBazz()之前发生吗?

2 个答案:

答案 0 :(得分:-1)

您所显示的代码中根本没有内存订购,因此保证成立。但是,通过fetch_sub成为release sequence的一部分,您可以在适当的位置使用宽松的排序并使其有效:

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()之前发生。

答案 1 :(得分:-1)

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,因为42到y的存储仅在存储到x存储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()之前发生。