gpt4 book ai didi

带有栅栏和获取/释放的 C++ memory_order

转载 作者:可可西里 更新时间:2023-11-01 16:39:35 28 4
gpt4 key购买 nike

我有以下 C++ 2011 代码:

std::atomic<bool> x, y;
std::atomic<int> z;

void f() {
x.store(true, std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_release);
y.store(true, std::memory_order_relaxed);
}

void g() {
while (!y.load(std::memory_order_relaxed)) {}
std::atomic_thread_fence(std::memory_order_acquire);
if (x.load(std::memory_order_relaxed)) ++z;
}

int main() {
x = false;
y = false;
z = 0;
std::thread t1(f);
std::thread t2(g);
t1.join();
t2.join();
assert(z.load() !=0);
return 0;
}

在我的计算机体系结构类(class)中,我们被告知此代码中的断言始终为真。但是现在仔细阅读之后,我真的不明白为什么会这样。

据我所知:

  • 带有“memory_order_release”的栅栏将不允许之前的存储在它之后执行
  • 带有“memory_order_acquire”的栅栏将不允许在它之后执行的任何加载在它之前执行。

如果我的理解是正确的,为什么不能出现下面的一系列 Action 呢?

  1. 在 t1 内,y.store(true, std::memory_order_relaxed); 被调用
  2. t2 完全运行,并且在加载“x”时会看到“false”,因此不会在一个单元中增加 z
  3. t1 完成执行
  4. 在主线程中,断言失败,因为 z.load() 返回 0

我认为这符合“获取”-“发布”规则,但是,例如在这个问题的最佳答案中:Understanding c++11 memory fences这与我的情况非常相似,它暗示我的操作序列中的第 1 步之类的事情不会在“memory_order_release”之前发生,但没有详细说明其背后的原因。

我对此感到非常困惑,如果有人能对此有所了解,我将非常高兴:)

最佳答案

每种情况下具体发生的情况取决于您实际使用的处理器。例如,x86 可能不会断言这一点,因为它是一个缓存一致的架构(你可以有竞争条件,但是一旦一个值从处理器写入缓存/内存,所有其他处理器都会读取该值 -当然,不会阻止另一个处理器在之后立即写入不同的值,等等)。

因此假设它运行在 ARM 或类似的处理器上,但不能保证其本身缓存一致:

因为对x的写入是在memory_order_release之前完成的,t2循环不会退出while(y...)直到x 也是如此。这意味着当 x 稍后被读取时,它保证是一个,所以 z 被更新。我唯一的小问题是关于你是否也不需要 zrelease ... 如果 main 在不同的平台上运行处理器比 t1t2,那么 z 可能仍然在 main 中有一个陈旧的值。

当然,如果你有一个多任务操作系统(或者只是中断做足够的事情等),那不能保证发生 - 因为如果运行 t1 的处理器刷新了它的缓存,那么 t2 可能会读取新的值X。

正如我所说,这不会对 x86 处理器(AMD 或 Intel 处理器)产生影响。

因此,一般性地解释屏障指令(也适用于 Intel 和 AMD process0rs):

首先,我们需要明白,虽然指令可以无序地开始和结束,但处理器确实对顺序有一个普遍的“理解”。假设我们有这个“伪机器代码”:

 ...
mov $5, x
cmp a, b
jnz L1
mov $4, x

L1: ...

处理器可以在完成“jnz L1”之前推测性地执行 mov $4, x - 因此,为了解决这个问题,处理器必须回滚 mov $4, xjnz L1 被采用的情况下。

同样,如果我们有:

 mov $1, x
wmb // "write memory barrier"
mov $1, y

处理器有规则说“在完成之前的所有存储之前,不要执行任何在 wmb 之后发出的存储指令”。这是一个“特殊”指令——它的存在是为了保证内存排序的精确目的。如果它不这样做,那么你的处理器就坏了,而且设计部门的某个人“他的屁股在线”。

同样,“读取内存屏障”是一条指令,由处理器的设计者保证,在我们完成屏障指令之前的挂起读取之前,处理器不会完成另一次读取。

只要我们不是在研究“实验性”处理器或一些无法正常工作的劣质芯片,它就会以这种方式工作。它是该指令定义的一部分。没有这样的保证,就不可能(或者至少极其复杂和“昂贵”)实现(安全的)自旋锁、信号量、互斥量等。

通常还存在“隐式内存障碍”——也就是说,即使没有,也会导致内存障碍的指令。软件中断(“INT X”指令或类似指令)倾向于执行此操作。

关于带有栅栏和获取/释放的 C++ memory_order,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14492292/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com