1. 为什么需要原子操作:从多线程数据竞争说起
我第一次遇到原子操作的需求是在开发一个高并发交易系统时。当时系统里有个简单的计数器,用来统计成功交易次数,理论上所有线程完成操作后这个数字应该准确无误。但实际运行时,计数器经常出现莫名其妙的数值,有时甚至会少统计几百笔交易。这就是典型的数据竞争(Data Race)问题。
在C++多线程环境中,当多个线程同时读写同一个变量时,如果没有适当的同步机制,就会产生不可预测的结果。比如最简单的i++操作,在底层实际上分为三个步骤:
- 从内存读取i的值到寄存器
- 寄存器中的值加1
- 将结果写回内存
如果两个线程同时执行i++,可能会出现以下交错执行的情况:
code复制线程A:读取i(值为0)
线程B:读取i(值为0)
线程A:加1得到1
线程B:加1得到1
线程A:写入1
线程B:写入1
最终i的值是1而不是预期的2。这就是为什么在我的交易系统中会出现计数不准的问题。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 原子操作的基本用法与性能对比
2.1 传统互斥锁方案
最直观的解决方案是使用互斥锁(mutex):
cpp复制std::mutex mtx;
int counter = 0;
void increment() {
mtx.lock();
counter++; // 临界区
mtx.unlock();
}
这种方法确实能保证正确性,但性能开销很大。在我的测试中,20个线程各执行10万次递增操作:
- 无锁版本:13ms (结果错误)
- 互斥锁版本:357ms (结果正确)
互斥锁的主要问题在于:
- 锁获取/释放需要系统调用
- 线程阻塞会导致上下文切换
- 临界区较大时严重影响并发度
2.2 原子操作解决方案
C++11引入了
cpp复制std::atomic<int> counter(0);
void increment() {
counter++; // 原子操作
}
同样的测试条件下:
- 原子操作版本:170ms (结果正确)
原子操作的性能优势主要来自:
- 硬件级别的原子指令(如x86的LOCK前缀)
- 无锁设计,避免线程阻塞
- 编译器优化保证
