以下代码写入原子变量A、B,并在另一个线程中以相反的顺序读取它们:
#include <atomic>
#include <thread>
// Initially.
std::atomic<int> A = std::atomic<int>(0);
std::atomic<int> B = std::atomic<int>(0);
// Thread 1
void write()
{
A.store(1, std::memory_order_release); // line 1
B.store(1, std::memory_order_release); // line 2
}
// Thread 2
void read()
{
int valB = B.load(std::memory_order_acquire); // line 3
int valA = A.load(std::memory_order_acquire); // line 4
}
int main()
{
std::thread t1(write), t2(read);
t1.join();
t2.join();
return 0;
}
理想情况下,给定原子B上的释放-获取顺序,第3行上的加载应该与第1行上的存储的完成同步,因为它恰好在第2行上的释放操作之前。
从线程A的Angular 来看,在原子存储之前发生的所有内存写入**(非原子和宽松原子)**在线程B中成为可见的副作用。
那么,B上的释放-获取排序,不会影响其他变量上的原子操作,这些变量不是宽松的内存排序?
在线程2中read()结束后,是否有可能有then,valB = 1
,valA = 0
的值,因为B的内存排序并不关心其他未放松的原子操作?
1条答案
按热度按时间kwvwclae1#
在线程2中read()结束后,是否可以让valB = 1,valA = 0的值,因为B的内存排序不关心其他未放松的原子操作?
不,这不可能。正如你所引用的:
从线程A的Angular 来看,在原子存储之前发生的所有内存写入(非原子和宽松原子)在线程B中成为可见的副作用。
因此,当您从
B
读取1
时,对A
的写操作在阅读线程中可见,因此接下来对A
的读操作也必须返回1
。你强调的括号中的部分可能更好地措辞为“(* 包括 * 非原子和松弛原子)”--这是你被绊倒的地方吗?
标准是怎么说的?
该标准的相关部分是原子顺序1.4:
对原子对象M执行释放操作的原子操作A与对M执行获取操作的原子操作B同步
intro.multithread中描述了“同步于”的含义:
如果A * 与 * B [...]同步,则线程间求值A * 发生在 * 求值B之前
求值A * 发生在 * 求值B之前(或者等价地,B发生在A之后),如果
“发生于”是可传递的:
A.store()
发生在B.store()
之前(排序在B.store()
之前),发生在B.load()
之前(与B.load()
同步),发生在A.load()
之前(排序在A.load()
之前)。该标准明确规定了原子对象:
如果原子对象M * 上的副作用X发生在M的值计算B之前,那么评估B将从X或从在M的修改顺序中跟随X的副作用Y获取其值。
因此,由于
A.store(1)
发生在A.load()
之前,并且没有其他候选操作Y,因此A.load()
必须返回1。我也可以推荐this blog post。