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

连续原子操作的Acquire-Release内存模型相关技术疑问

原子变量Release-Acquire顺序对非Relaxed原子操作的影响

以下代码在一个线程中写入原子变量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的release-acquire顺序,线程2中第3行的load操作应与线程1中第1行的store操作完成同步,因为第1行操作发生在第2行的release操作之前。但参考文档提到(重点为提问者所加):

从线程A的视角来看,所有发生在原子store操作之前的内存写入**(非原子操作和relaxed原子操作)**,都会成为线程B中可见的副作用。

那么,针对B的release-acquire顺序是否不会影响其他非relaxed内存顺序的原子操作?是否可能在线程2的read()执行结束后出现valB = 1、valA = 0的情况?因为B的内存顺序不关心其他非relaxed的原子操作?我们是否需要使用sequential consistency?


核心结论

是的,完全可能出现valB=1且valA=0的情况,这是C++内存模型允许的合法行为。Release-Acquire同步在这里并没有保证A的store操作对线程2可见,具体原因如下:

1. Release-Acquire的作用边界

你引用的文档是简化表述,完整的C++内存模型中,release操作的"之前的写入"包含当前线程中所有happens-before该release操作的内存操作——无论操作是原子还是非原子,也不管原子操作的内存顺序。但这里的关键问题是:线程1中A的store(line1)和B的store(line2)之间没有强制的顺序约束。

在单线程内部,代码顺序是先写A再写B,但memory_order_release并不禁止CPU对这两个store操作做重排序(只要不违反线程内的happens-before规则)。也就是说,从其他线程的视角看,B的store完全可能先于A的store完成。

2. 为什么Release-Acquire没生效

当线程2通过acquire load读到B=1时,它只能保证:线程1中所有happens-before B的release store的操作,对线程2可见。但如果线程1中A的store被重排到了B的store之后,那么A的store并不在这个"happens-before"集合里,自然无法保证对线程2可见。

3. 如何确保预期行为

如果想保证线程2读到B=1时,一定能读到A=1,有两种可靠方案:

  • 方案一:使用顺序一致性(sequential consistency)
    将所有原子操作的内存顺序改为std::memory_order_seq_cst,这种顺序会强制所有原子操作的全局总顺序,禁止任何破坏程序逻辑顺序的重排序。但它的性能开销是所有内存顺序中最大的。
    // Thread1
    A.store(1, std::memory_order_seq_cst);
    B.store(1, std::memory_order_seq_cst);
    // Thread2
    int valB = B.load(std::memory_order_seq_cst);
    int valA = A.load(std::memory_order_seq_cst);
    
  • 方案二:显式建立A和B的happens-before关系
    不需要全局顺序,只要确保线程1中A的store一定happens-before B的store。可以通过插入内存屏障,或者让B的store依赖于A的操作结果来实现:
    void write()
    {
        A.store(1, std::memory_order_release);
        std::atomic_thread_fence(std::memory_order_release); // 强制之前的操作完成后再执行后续store
        B.store(1, std::memory_order_release);
    }
    
    或者通过依赖关系禁止重排:
    void write()
    {
        A.store(1, std::memory_order_relaxed);
        // 通过A的load结果作为B的store值,建立依赖关系,禁止重排
        B.store(A.load(std::memory_order_relaxed), std::memory_order_release);
    }
    

4. 总结

Release-Acquire同步只保证"release操作之前已完成的操作"对acquire线程可见,但它不保证release操作之前的原子操作不会被重排到release操作之后。如果需要跨线程的操作顺序保证,要么使用顺序一致性,要么显式建立操作之间的happens-before关系。


内容的提问来源于stack exchange,提问作者Akash

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 18:40:38