C++中的std::atomic有什么用?(原子操作防止竞态条件)

12次阅读

std::atomic 解决线程下变量读-改-写竞态问题,通过硬件/编译器级原子操作保证 load/store、++、CAS 等操作不可分割;支持平凡可复制的小型类型,需显式声明,不替代 mutex 的多步逻辑保护。

C++中的std::atomic有什么用?(原子操作防止竞态条件)

std::atomic 能解决什么问题?

当多个线程同时读写同一个变量(比如 int counter = 0;),++counter 不是原子的:它实际包含「读取→加1→写回」三步。两个线程可能同时读到 0,各自加 1 后都写回 1,结果丢失一次递增。这就是竞态条件。std::atomic 把这类操作打包成不可打断的单步指令,让读-改-写变成一个整体,从硬件或编译器层面阻止交错。

哪些操作能用 std::atomic 安全替代?

常见整型指针类型支持直接替换,但必须显式声明为 std::atomic,不能对普通变量临时套用原子函数。以下操作默认是原子的(无需额外锁):

  • load()store(value) —— 带内存序的读/写
  • operator++()operator+=() —— 复合赋值(如 counter++
  • compare_exchange_weak(expected, desired) —— CAS(Compare-And-Swap),实现无锁数据结构的核心

注意:std::atomicstd::atomicstd::atomic 是标准保证可锁自由(lock-free)的;而 std::atomic<:string> 不合法 —— 类型必须是平凡可复制(trivially copyable)且大小适中(通常 ≤ 指针宽度)。

不指定内存序会怎样?

默认使用 std::memory_order_seq_cst(顺序一致性),最安全但也最慢 —— 编译器和 CPU 会插入大量屏障防止重排。实际中可根据场景降级:

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

  • 仅需避免自身读写重排?用 std::memory_order_relaxed(如计数器)
  • 需要“写后读可见”?用 std::memory_order_acquire(读) + std::memory_order_release(写)配对
  • CAS 失败时通常用 relaxed,成功路径才考虑更强序

错误示例:

std::atomic flag{0}; // 线程 A: flag.store(1, std::memory_order_relaxed); // 这个 store 可能被重排到后面语句之后 data_ready = true;  // 线程 B: if (flag.load(std::memory_order_relaxed) == 1) {     use(data_ready); // data_ready 可能还是 false! }

这里必须用 release/acquire 配对才能保证 data_ready 的写入对 B 可见。

std::atomic 不能替代 mutex 的地方

它只保单个变量的读写原子性,无法保护多步逻辑。比如「检查值是否为 0,是则设为 1」需要 CAS 循环,而「先删节点再更新头指针再释放内存」这种多对象协作,仍需 std::mutex 或更高级的无锁设计。

另外,std::atomic 对象本身不可拷贝(只有移动或赋值),也不能作为容器元素直接存储(需用 std::vector<:atomic>*> 或包装成 Struct);调试时打印其值要用 .load(),直接 std::cout 会编译失败。

text=ZqhQzanResources