C++怎么用原子操作 C++中atomic并发编程【前沿】

2次阅读

std::atomic必须显式初始化,否则值未定义;自定义类型需满足trivially copyable且对齐足够;load/store须指定memory_order,默认seq_cst性能差;atomic_flag是唯一强制lock-free的原子类型;指针算术需谨慎,避免隐式操作。

C++怎么用原子操作 C++中atomic并发编程【前沿】

std::atomic 必须显式初始化,否则值未定义

很多刚用 std::atomic 的人会写 std::atomic<int> counter;</int> 就直接 ++,结果发现第一次读出来是随机大数。这不是 bug,是 c++ 标准规定:默认构造的 std::atomic 对象不保证初始化为零,它只做 trivial 构造,内存内容未被清零。

  • 正确做法是显式初始化:std::atomic<int> counter{0};</int>std::atomic<int> counter = {0};</int>
  • 对于自定义类型(如 Struct Point { int x,y; };),只有满足 is_trivially_copyable_v<point></point> 且对齐足够时才能用 std::atomic<point></point>;否则编译失败或退化为锁实现
  • 注意:std::atomic<bool></bool> 默认构造后值是 false,这是唯一例外,但别依赖——显式写 {false} 更安全

load/store 不能省略 memory_order 参数,否则默认是 seq_cst

seq_cst(sequential consistency)最安全,但也最重。在高频计数、无依赖的标志位等场景下,它会强制刷新所有缓存行,拖慢性能。很多人以为“不写参数就没事”,其实是在默默付出代价。

  • 读操作常用 .load(std::memory_order_acquire)(如检查运行标志)
  • 写操作常用 .store(true, std::memory_order_release)(如设置完成状态)
  • 自增/自减等读-改-写操作默认也是 seq_cst,高频循环里建议用 .fetch_add(1, std::memory_order_relaxed) ——前提是不依赖该操作与其他内存访问的顺序
  • 混用不同 order 时,必须成对出现(acquire/release 配对,consume 已基本弃用)

std::atomic_flag 是唯一无锁且保证 lock-free 的原子类型

其他 std::atomic<t></t> 类型是否 lock-free 取决于平台和 T 的大小。比如 std::atomic<uint64_t></uint64_t> 在某些 32 位 ARM 上会回退到内部互斥锁,导致意外阻塞。而 std::atomic_flag 是标准强制要求 lock-free 的,适合做轻量级自旋锁。

  • 它只有两个状态:test_and_set()(置位并返回旧值)和 clear()(清零),没有 load/store
  • 必须用 ATOMIC_FLAG_INIT 初始化(C++20 起可直接用 {}
  • 别试图用它存业务数据——它不是容器,只是个“门闩”。真要自旋锁,优先考虑 std::mutex;只有极低延迟场景才手写 atomic_flag 自旋

不要对 std::atomic 指针做算术运算,除非你清楚它在做什么

std::atomic<int> p;</int> 支持 p += 2;,但这不是原子地加 2 个字节,而是加 2 * sizeof(int) 字节。更危险的是:它底层调用的是 fetch_add,而指针类型fetch_add 在部分平台(如旧版 GCC + ARM)可能不 lock-free,甚至触发陷阱指令。

立即学习C++免费学习笔记(深入)”;

  • 如果只是想原子更新指针值(如无锁头),用 .exchange().compare_exchange_weak() 更明确、更可控
  • 避免 ++p 这类隐式操作——可读性差,且容易误以为是“线程安全的遍历”
  • 指向动态对象的原子指针,务必确保对象生命周期长于所有可能的读取;std::shared_ptr 的原子操作(std::atomic_shared_ptr)在 C++20 才有,之前只能靠 std::atomic<t></t> + 手动管理

复杂点在于:原子操作的安全性不只取决于函数名,还绑定在内存序、类型尺寸、目标架构三者交集上。一个在 x86 上跑得飞快的 relaxed 计数器,在 ARM 或 RISC-V 上可能突然变慢,甚至行为不同。别只测逻辑,一定要在目标平台上验证 lock-free 属性(查 is_lock_free())。

text=ZqhQzanResources