C++ 11 你能在不同线程中通过引用安全地传递和访问 std::atomics 吗

C++ 11 can you safely pass and access std::atomics by reference in different threads

我想知道您是否可以通过对线程的引用传递原子,并且 .load 和 .store 操作仍然是线程安全的。例如:

#include <thread>
#include <atomic>
#include <cstdlib>

void addLoop(std::atomic_int& adder)
{
    int i = adder.load();
    std::srand(std::time(0));
    while(i < 200)
    {
        i = adder.load();
        i += (i + (std::rand() % i));
        adder.store(i);
    }
}

void subLoop (std::atomic_int& subber)
{
    int j = subber.load();
    std::srand(std::time(0));
    while(j < 200)
    {
        j = subber.load();
        j -= (j - (std::rand() % j));
        subber.store(j);
    }
}

int main()
{
    std::atomic_int dummyInt(1);
    std::thread add(addLoop, std::ref(dummyInt));
    std::thread sub(subLoop, std::ref(dummyInt));
    add.join();
    sub.join();
    return 0;
}

当 addLoop 线程将新值存储到 atomic 中时,如果 subLoop 使用加载和存储函数访问它,它最终会成为未定义状态吗?

根据[intro.races]/20.2,

The execution of a program contains a data race if it contains two potentially concurrent conflicting actions, at least one of which is not atomic, and neither happens before the other, except for the special case for signal handlers described below. Any such data race results in undefined behavior.

根据[intro.races]/2,

Two expression evaluations conflict if one of them modifies a memory location (4.4) and the other one reads or modifies the same memory location.

通过引用访问原子变量不会引入任何额外的访问或修改,因为引用不占用内存位置。因此,当这些操作通过引用发生时,执行潜在的并发原子操作仍然是安全的。

事实上,在C++求值的抽象模型中,通过名称访问对象和通过绑定到对象的引用变量访问对象没有区别。两者都只是引用对象的左值。

当心 std::atomic_init 函数,它是非原子的:

std::atomic<int> x;
void f(std::atomic<int>& r) {
    std::atomic_init(&r, 0);
}
void g(std::atomic<int>& r) {
    r = 42;
}

在上面的代码中,如果 fg 运行 在不同的线程中并且都访问原子变量 x,就会发生数据竞争,因为一个的操作不是原子的。但是,这与像这样触发数据竞争没有什么不同:

std::atomic<int> x;
void f() {
    std::atomic_init(&x, 0);
}
void g() {
    x = 42;
}

其中不涉及引用。