跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

C++ std::atomic::load与store _ 内存顺序(memory_order)详解【详解】

std::atomic::load默认使用std::memory_order_seq_cst,但性能开销大;应根据同步需求选用acquire/release或relaxed;store()与operator=内存序不同;compare_exchange_weak需分别指定成功和失败的内存序。 std::atomic::load 默认用的是 std::memory_order_seq_cst 绝大多数情况下,
load()
不传
order
参数,就等价于
load(std::memory_order_seq_cst)
。这不是“最安全”的选择,而是 C++ 标准规定的默认行为:它强制所有线程看到的操作顺序全局一致,代价是可能触发全内存栅栏(full fence),在弱一致性架构(如 ARM、RISC-V)上开销明显。 如果你只是读一个标志位(比如
std::atomic ready
),且写端用的是
store(true, std::memory_order_release)
,那读端用
load(std::memory_order_acquire)
就足够了——既保证数据可见性,又避免不必要同步开销。 不要默认以为 “seq_cst 就是必须的”,尤其在高频轮询或性能敏感路径中
std::memory_order_relaxed
可用于计数器累加、统计类场景,但不能用于同步逻辑(比如等待某个状态) 用
load()
时若没指定 order,编译器不会报错,但可能掩盖同步漏洞 store() 和 operator= 的内存序行为不完全等价
store()
明确支持传入任意
std::memory_order
,而
operator=
固定使用
std::memory_order_seq_cst
。这意味着: 想用
std::memory_order_release
发布数据(如写完
data
后设
ready = true
),必须写
ready.store(true, std::memory_order_release)
,不能写
ready = true
operator=
看起来简洁,但会隐式引入更强的顺序约束,可能拖慢性能,也容易让后续维护者误判同步意图 代码审查时,
store()
和
load()
是信号:这里有意图地控制内存序;
=
和隐式转换则缺乏这种表达力 load() 配合 acquire、store() 配合 release 才构成同步对 单个
load(std::memory_order_acquire)
或
store(std::memory_order_release)
没有意义——它们必须成对出现,跨线程才能建立 happens-before 关系。 立即学习 “ C++免费学习笔记(深入) ”; C知道 CSDN推出的一款AI技术问答工具 下载 典型模式:
// 线程 A(生产者) data = 42; // 普通写 ready.store(true, std::memory_order_release); // release store

// 线程 B(消费者) while (!ready.load(std::memory_order_acquire)) { / 等待 / } // acquire load // 此处能安全读 data == 42

release store 保证其之前的普通写(
data = 42
)不会被重排到它之后 acquire load 保证其之后的普通读(
data
)不会被重排到它之前 两个操作通过同一个原子变量(
ready
)关联,才构成同步点 如果读端用了
relaxed
,即使写端是
release
,也不能保证看到
data
的新值 compare_exchange_weak 的 memory_order 参数要分两组传
compare_exchange_weak()
是 RMW 操作,它内部包含一次 load 和一次 store,所以需要两个内存序参数: 成功时的内存序(即 store 部分):第二个参数
success
失败时的内存序(即 load 部分):第三个参数
failure
常见写法:
std::atomic counter{0}; int expected = 0; counter.compare_exchange_weak(expected, 1, std::memory_order_acq_rel, // 成功:既是 acquire(读旧值)又是 release(写新值) std::memory_order_acquire); // 失败:只做 acquire load
漏掉第三个参数会导致编译失败(不是默认值) 多数场景下,
failure
用
std::memory_order_acquire
足够;
success
用
std::memory_order_acq_rel
可支持后续同步 别把两个参数都设成
relaxed
——除非你清楚自己在放弃同步语义 实际写多线程代码时,最容易忽略的不是语法,而是“哪个操作和哪个操作构成同步对”。
load()
和
store()
的内存序必须跨线程配对理解,而不是孤立看单个调用。

相关文章