You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

加载内存中的指针时是否需要使用内存获取屏障?

加载内存中的指针时是否需要使用内存获取屏障?

这个问题问得很实际,咱们先从你给出的第一个经典例子入手,理清release-acquire屏障的核心作用,再回头分析你拿不准的场景。

首先看第一个标准同步案例:

int x = 0;
int y = 0;

void thread1(void)
{
    x = 1;
    atomic_store_explicit(&y, 1, memory_order_release);
}

void thread2(void)
{
    int tmp_y = atomic_load_explicit(&y, memory_order_acquire);
    int tmp_x = x;

    if (tmp_y)
        assert(tmp_x); // must be ok
}

这里的memory_order_release和memory_order_acquire是配对的同步机制:当thread2通过acquire加载读到y=1时,thread1中所有在release存储y之前的写操作(也就是x=1),对thread2中acquire加载之后的读操作(tmp_x=x)都是完全可见的,所以断言一定成立,这是release-acquire语义的核心价值。

接下来分析你拿不准的场景:

int x = 0;
int *px = NULL;

void thread1(void)
{
    x = 1;
    atomic_store_explicit(&px, &x, memory_order_release);
}

void thread2(void)
{
    int *p = atomic_load_explicit(&px, memory_order_relaxed); // Do we need memory_order_acquire here?

    if (p)
        assert(*p); // is it ok?
}

答案是必须把这里的memory_order_relaxed换成memory_order_acquire,否则断言不一定成立,具体原因如下:

  • thread1里的memory_order_release确实保证了x=1不会被编译器或CPU重排到atomic_store_explicit(&px, &x)之后,但这只是thread1内部的顺序约束,没法直接作用到thread2。
  • 如果thread2用memory_order_relaxed加载px,这个加载操作和后续的*p(也就是读x)之间没有任何同步绑定:
    1. 编译器可能会把*p的读操作重排到加载px之前,导致读到x的初始值0;
    2. 即使线程2读到了非NULL的px,CPU的缓存一致性机制也不能保证x的最新值已经同步到thread2的缓存中——没有acquire屏障触发缓存同步的话,thread2可能还会看到x的旧值。

只有当thread2用memory_order_acquire加载px时,一旦读到了thread1写入的&x,就会和thread1的release存储建立跨线程的同步关系,此时thread1中所有在release存储之前的写操作(x=1),对thread2中acquire加载之后的读操作(*p)都是可见的,断言才能保证稳定成立。

简单来说:你可以把指针px看作是一个“就绪信号”,发信号的thread1用release保证“信号发出前所有准备工作都完成”,接收信号的thread2必须用acquire保证“看到信号后,能获取到信号发出前的所有准备结果”,二者缺一不可。

备注:内容来源于stack exchange,提问作者untitled

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 11:20:27