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

C++11中用Acquire/Release替换Seq_CST后断言是否会失败?

问题解答:将seq_cst替换为acquire/release后assert是否会触发失败?

先看调整后的代码:

#include <thread> 
#include <atomic> 
#include <cassert> 

std::atomic<bool> x = {false}; 
std::atomic<bool> y = {false}; 
std::atomic<int> z = {0}; 

void write_x() { 
    x.store(true, std::memory_order_release); 
} 

void write_y() { 
    y.store(true, std::memory_order_release); 
} 

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

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

int main() { 
    std::thread a(write_x); 
    std::thread b(write_y); 
    std::thread c(read_x_then_y); 
    std::thread d(read_y_then_x); 
    a.join(); 
    b.join(); 
    c.join(); 
    d.join(); 
    assert(z.load() != 0); 
}

答案是:这个assert确实有可能触发失败。下面结合内存序的特性来解释:

我们先对应你提到的几个内存序约束来分析:

  • std::memory_order_seq_cst确实能阻止StoreLoad重排,但换成acquire/release后就没有这个保障了,这也是问题的核心。
  • acquire确实能阻止LoadLoad重排:比如线程c里的x.load(acquire)之后的y.load(acquire)绝对不会被重排到x的load之前,线程d同理,这部分没问题。
  • release确实能阻止StoreStore重排:但这里两个写线程都只有一个store操作,所以这个约束在当前代码里没发挥额外作用。

关键问题在于,acquire/release只建立了单向的可见性保证(写线程的操作对读到该store的读线程可见),但没有像seq_cst那样强制所有原子操作遵循一个全局的总执行顺序。这就可能出现以下场景:

  1. 线程a的x.store(release)的结果被线程c看到了,所以c退出自旋,但此时线程b的y.store(release)的结果还没同步到c所在的核心,c读到y仍然是false,不会递增z;
  2. 同时,线程b的y.store(release)的结果被线程d看到了,d退出自旋,但线程a的x.store(release)的结果还没同步到d所在的核心,d读到x仍然是false,也不会递增z;

最终z的值还是0,触发assert(z.load() != 0)失败。

而如果用seq_cst的话,所有原子操作都有一个全局总序,要么x的store在y的store之前,要么反过来,这样至少有一个读线程能看到两个变量都为true,z会被递增,assert不会失败。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:21:40