std::atomic提供原子操作避免数据竞争,适用于整型和指针类型,通过内存序控制同步强度与性能,常用于计数器、无锁结构,需注意伪共享和CAS争用问题。

在多线程编程中,数据竞争是常见问题。C++11引入了std::atomic来提供一种类型安全且高效的原子操作机制,帮助开发者避免使用互斥锁(mutex)也能实现线程安全。它适用于对基本数据类型的读写保护,比如整型、指针等。
std::atomic的基本用法
std::atomic模板类可以包装支持平凡拷贝的类型,最常用的是整数类型和指针类型。定义一个原子变量非常简单:
整型原子操作示例:
#include
#include
std::atomiccounter(0);
void increment() {
for (int i = 0; i < 1000; ++i) {
counter.fetch_add(1, std::memory_order_relaxed);
}
}
上面代码中,多个线程调用increment()函数不会导致数据竞争。fetch_add是原子加法操作,返回旧值。也可以使用++counter,它会被重载为原子递增。
立即学习“C++免费学习笔记(深入)”;
指针原子操作示例:
struct Node { int data; Node* next; };
std::atomic head(nullptr);
void push_node(Node* node) {
Node* old_head = head.load();
do {
node->next = old_head;
} while (!head.compare_exchange_weak(old_head, node));
}
这里使用compare_exchange_weak实现无锁栈的插入操作,是一种典型的CAS(Compare-And-Swap)模式。
内存序(Memory Order)的选择
std::atomic操作可以指定内存顺序,影响性能与同步强度。常用的有:
- std::memory_order_relaxed:仅保证原子性,不保证顺序。适合计数器等无需同步场景。
- std::memory_order_acquire:用于读操作,确保之后的读写不会被重排到该操作前。
- std::memory_order_release:用于写操作,确保之前的读写不会被重排到该操作后。
- std::memory_order_acq_rel:同时具有acquire和release语义。
- std::memory_order_seq_cst:默认选项,提供最严格的顺序一致性,但开销最大。
例如,若只关心计数,可用relaxed提升性能:
counter.fetch_add(1, std::memory_order_relaxed);
若需要同步多个变量,则应使用seq_cst或搭配acquire/release。
性能分析与适用场景
原子操作的性能远高于互斥锁,尤其在低争用场景下。由于不涉及内核态切换和上下文调度,原子指令通常在用户态完成。
性能对比关键点:
- 缓存行冲突:多个原子变量若位于同一缓存行,频繁修改会导致“伪共享”(False Sharing),显著降低性能。可通过填充对齐避免。
- CAS循环争用:高并发下,compare_exchange可能多次失败重试,退化为忙等待。此时需评估是否改用锁更高效。
- 平台差异:x86架构对顺序一致性支持较好,而ARM/PowerPC等弱内存模型架构对宽松内存序更敏感,需谨慎选择内存序。
一般建议:
- 简单计数、状态标志用std::atomic
+ relaxed。 - 实现无锁队列、栈等结构时,配合acquire/release控制同步。
- 避免在热点路径上频繁使用seq_cst,除非确实需要全局顺序一致。
基本上就这些。合理使用std::atomic能有效提升并发性能,但要注意内存序语义和硬件特性,避免误用带来隐患。











