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