从C++中的另一个线程读取指针

Ber*_*ard 4 c++ multithreading pointers atomicity

在下面的代码中,x由于原子线程围栏,线程2中的值将始终为10.

int x;
atomic<bool> b(false);

// thread 1:
x = 10;
atomic_thread_fence(memory_order_release);
b = true;

// thread 2:
while(!b){}
atomic_thread_fence(memory_order_acquire);
assert(x == 10); // x will always be 10
Run Code Online (Sandbox Code Playgroud)

但是在下面的代码中,*x线程2中的总是10?

int* x = new int;
atomic<bool> b(false);

// thread 1:
*x = 10;
atomic_thread_fence(memory_order_release);
b = true;

// thread 2:
while(!b){}
atomic_thread_fence(memory_order_acquire);
assert(*x == 10); // will *x always be 10?
Run Code Online (Sandbox Code Playgroud)

Max*_*kin 8

在这两种情况下10,无论是直接完成还是通过指针完成存储都没有区别.

你不需要这里的记忆围栏,因为b = true它本质上是b.store(true, std::memory_order_seq_cst)一个带栅栏的获取释放.

这样的内存顺序可以防止编译器重新排序操作周围的存储和加载,并使此前存储在此存储变为可见时对其他线程可见.

如果比较这两个函数的生成代码:

#include <atomic>

int x;
std::atomic<bool> b(false);

void f() {
    x = 10;
    std::atomic_thread_fence(std::memory_order_release);
    b = true;
}

void g() {
    x = 10;
    b = true;
}
Run Code Online (Sandbox Code Playgroud)

它完全相同:

f():
        movl    $10, x(%rip)
        movb    $1, b(%rip)
        mfence
        ret
g():
        movl    $10, x(%rip)
        movb    $1, b(%rip)
        mfence
        ret
Run Code Online (Sandbox Code Playgroud)

在您的特定情况下,在我看来,您只需要std::memory_order_release存储b以使存储x也可以被其他线程看到,栅栏是不必要的.即b.store(true, std::memory_order_release)是足够的在这里.消费者代码需要做b.load(std::memory_order_acquire).

标准互斥锁在锁定时获取内存顺序并在解锁时释放内存顺序(这是条件获取/释放的来源),不涉及任何围栏.

很少需要显式围栏,主要是硬件驱动程序.在用户空间模式中,由于对C++ 11内存模型的误解,通常会放置代码.栅栏是最昂贵的原子同步机制,这是它们被避免的主要原因.