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

C++ C++;11您能否在不同线程中通过引用安全地传递和访问std::atomics,c++,multithreading,c++11,pass-by-reference,atomic,C++,Multithreading,C++11,Pass By Reference,Atomic,我想知道您是否可以通过引用线程来传递原子,并且对于.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)

我想知道您是否可以通过引用线程来传递原子,并且对于.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;
}
#包括
#包括
#包括
void addLoop(标准::原子整数和加法器)
{
int i=加法器加载();
std::srand(std::time(0));
而(i<200)
{
i=加法器加载();
i+=(i+(标准::兰德()%i));
加法器存储器(i);
}
}
void子循环(std::atomic_int&subber)
{
int j=子对象加载();
std::srand(std::time(0));
而(j<200)
{
j=子比特负载();
j-=(j-(标准::兰德()%j));
子商店(j);
}
}
int main()
{
std::原子_int dummyInt(1);
线程添加(addLoop,std::ref(dummyInt));
std::thread sub(子循环,std::ref(dummyInt));
add.join();
sub.join();
返回0;
}

当addLoop线程将新值存储到原子中时,如果子循环要使用load and store函数访问它,它是否会成为未定义状态?

根据[intro.races]/20.2

如果一个程序的执行包含两个潜在的并发冲突操作,则该程序的执行包含一个数据竞争, 其中至少有一个不是原子的,而且两个都不是在另一个之前发生的,除了 下面描述的信号处理程序。任何这样的数据竞争都会导致未定义的行为


根据[intro.races]/2

如果其中一个修改内存位置(4.4),而另一个读取,则两个表达式求值将发生冲突 或修改相同的内存位置

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

实际上,在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;
}

没有引用的地方。

您的代码有错误,`j-=(j-(std::rand()%j));`可能会使j=0,然后转一个被零除的错误。使用
std::thread
参数的别名与引用单个全局对象的多个线程的工作方式没有任何区别——设置引用的顺序与启动线程的顺序有关,因此,您确实正确地为
std::atomic
对象添加了别名。之后,取决于对象如何处理并发访问(对于
std::atomic
,这很好,对于大多数其他类型,甚至都不要尝试)
std::atomic<int> x;
void f() {
    std::atomic_init(&x, 0);
}
void g() {
    x = 42;
}