贝利信息

C++并发编程高阶指南:atomic、mutex与无锁结构性能对比【多线程优化】

日期:2026-01-23 00:00 / 作者:尼克
std::atomic 在简单变量(如 int、bool)的单次读写或原子运算(如 fetch_add)且无需多变量协同时比 std::mutex 快,因其避免系统调用和上下文切换,常编译为单条 CPU 原子指令;但复杂类型或错误使用 memory_order 会丧失优势甚至引发未定义行为。

atomic 在什么场景下比 mutex 快?

当操作是简单读写(如 intbool、指针)且不涉及多变量协同时,std::atomic 通常比 std::mutex 快得多——它避免了系统调用和上下文切换开销,底层常映射为单条 CPU 指令(如 x86lock xadd)。

但要注意:不是所有 atomic 操作都“轻量”。std::atomic<:shared_ptr>> 或自定义类型(需满足 trivially copyable 且大小 ≤ 指令原子宽度)的 load/store 可能退化为内部锁实现,性能反而不如显式 mutex

mutex 何时不可替代?

任何需要保护一段逻辑(而非单个变量)、或涉及 I/O、内存分配、异常抛出、非平凡构造/析构的操作,std::mutex(或更细粒度的 std::shared_mutex)仍是唯一选择。

比如在容器中插入元素:std::vector::push_back 可能触发重分配,这绝非原子操作;又比如日志写入函数需格式化字符串再写文件,中间步骤无法拆解为原子指令。

无锁结构(lock-free)真的更快吗?

“无锁”不等于“无开销”,而是指没有线程因等待锁而被阻塞。但实际性能取决于具体实现与硬件支持。例如 boost::lockfree::queue 在低争用下表现优异,但在高争用下可能因大量 CAS 失败导致缓存行乒乓(cache line bouncing),反不如带自旋优化的 std::mutex

更关键的是:无锁编程极易出错。一个典型的错误是 ABA 问题——某值从 A→B→A,CAS 误判为未变。标准库中只有 std::atomic::compare_exchange_weak 提供基础支持,但需手动管理版本号或使用 std::atomic<:shared_ptr> 配合引用计数规避。

如何实测三者真实开销?

别依赖理论模型。用 std::chrono::high_resolution_clock 测端到端耗时,更要关注 perf stat -e cache-misses,context-switches,instructions,cycles 这类指标。尤其注意:

#include 
#include 
#include 
#include 

alignas(64) std::atomic atomic_counter{0};
alignas(64) int raw_counter = 0;
alignas(64) std::mutex mtx;
int mutex_counter = 0;

void inc_atomic() {
    for (int i = 0; 

i < 100000; ++i) atomic_counter.fetch_add(1, std::memory_order_relaxed); } void inc_mutex() { for (int i = 0; i < 100000; ++i) { std::lock_guard lk(mtx); ++mutex_counter; } } // 注意:raw_counter 直接 ++ 是未定义行为,仅作对比基线(必错)
真正难的从来不是选 atomic 还是 mutex,而是判断哪些操作必须原子、哪些可以放松、哪些根本不需要同步——这得靠对数据流和线程边界的清醒认知,而不是套模板。