c++ 两个连续原子操作的获取-释放内存模型

sirbozc5  于 2023-02-10  发布在  其他
关注(0)|答案(1)|浏览(181)

以下代码写入原子变量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 = 1valA = 0的值,因为B的内存排序并不关心其他未放松的原子操作?

kwvwclae

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在B之前排序,或
  • 线程间在B之前发生。

“发生于”是可传递的: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

相关问题