C++中的原子操作(atomic)如何使用?(多线程安全)

原子操作是C++多线程中保障单变量原子性读写的机制,通过std::atomic实现无锁线程安全;支持隐式/显式内存序、compare_exchange及atomic_flag等,但不适用于多变量协同或浮点复合运算。

原子操作是C++多线程编程中保证变量读写不被中断、避免数据竞争的核心机制。用好std::atomic,不需要锁也能实现线程安全的简单共享变量访问。

基础用法:声明和初始化

把普通类型换成对应原子类型即可,比如intstd::atomic_int,或更通用的std::atomic

  • std::atomic counter{0}; —— 值初始化为0,支持所有基本整型、指针、以及满足 trivially copyable 的自定义类型(需额外注意对齐和大小限制)
  • 不能直接用拷贝构造或赋值(如auto x = counter;会调用隐式转换,实际是load());也不能传给需要非const引用的函数
  • 推荐用std::atomic模板,比内置别名(如atomic_int)更清晰、可移植性更好

读写操作:默认隐式 vs 显式控制内存序

最常用的是隐式操作:counter++counter.load()counter.store(42)counter.exchange(100)。它们默认使用std::memory_order_seq_cst(顺序一致性),最安全也稍慢。

  • 若需更高性能且逻辑允许,可显式指定内存序,例如:counter.fetch_add(1, std::memory_order_relaxed)(仅保证原子性,不约束前后指令重排)
  • 对计数器、标志位等简单场景,relaxed常够用;涉及状态协同(如生产者-消费者)时,常用acquire/release
  • 误用弱内存序可能导致逻辑错误,初学者建议先全用默认,再按需优化

常见组合操作:compare_exchange 和 flag

compare_exchange_weakcompare_exchange_strong是实现无锁结构的基础,用于“比较并交换”:

  • 典型模式:int expected = counter.load(); do { /* 修改expected */ } while (!counter.compare_exchange_weak(expected, new_value));
  • weak可能虚假失败(spuriously fail),适合循环重试;strong不虚假失败,但某些平台开销略大
  • 还有轻量级布尔标记:std::atomic_flag,只支持test_and_set()clear(),是唯一保证无锁(lock-free)的原子类型,适合实现自旋锁

注意事项和限制

原子类型不是万能锁替代品:

  • 仅对单个对象有效;多个相关变量无法靠多个原子操作自动同步(比如x和y要一起更新,就得用mutex或自定义原子结构)
  • 不支持浮点类型的fetch_add等复合操作(C++20起部分编译器扩展支持,但非标准)
  • is_lock_free()检查是否真正无锁(尤其在结构体上),否则底层可能用互斥量模拟,失去性能优势
  • 避免在原子变量上取地址(&counter非法),也不支持sizeof以外的指针运算

基本上就这些。用对场景、选对内存序、避开多变量耦合——原子操作就能既安全又高效。