C++ 11 您可以通过不同线程中的引用安全地传递和访问 std::atomics 吗?

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

本文关键字:访问 std atomics 安全 可以通过 线程 引用 C++      更新时间:2023-10-16

我想知道您是否可以通过引用线程来传递原子,并且对于 .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 线程将新值存储到原子中时,如果 subLoop 使用加载和存储函数访问它,它最终会成为未定义的状态吗?

根据 [intro.races]/20.2,

如果程序的执行包含

两个潜在的并发冲突操作,则程序的执行包含数据争用, 其中至少一个不是原子的,并且两者都不会先于另一个发生,除了 信号处理程序如下所述。任何此类数据争用都会导致未定义的行为。

根据[介绍种族]/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;
}

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

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

不涉及引用的地方。