我想知道你是否可以通过引用传递一个原子线程,并且.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使用加载和存储函数访问它,它最终会成为未定义状态吗?
答案 0 :(得分:1)
根据[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;
}
在上面的代码中,如果f
和g
在不同的线程中运行并且都访问原子变量x
,则可能发生数据争用,因为其中一个操作不是原子的。但是,这与触发数据竞争没有什么不同:
std::atomic<int> x;
void f() {
std::atomic_init(&x, 0);
}
void g() {
x = 42;
}
不涉及任何参考。