一、先问一个核心问题
前面我们已经学了:
- mutex ------ 保护共享资源
- condition_variable ------ 线程协作
那为什么还需要:
cpp
std::atomic
因为有一种场景:
你不是在保护"复杂资源"
你只是想保证"某个状态是安全的"
二、atomic 解决什么问题?
atomic 解决两件事:
1️⃣ 避免数据竞争
2️⃣ 保证可见性
它适合:
- 计数器
- 状态标志
- 停止信号
- 简单数值更新
三、先看一个错误示例
cpp
bool stop = false;
void worker() {
while (!stop) {
// do work
}
}
主线程:
cpp
stop = true;
你以为 worker 会停?
⚠ 可能不会。
原因:
- 编译器可能优化
- CPU 缓存可能导致不可见
- 多核缓存不同步
这叫:
内存可见性问题
四、正确写法:std::atomic
cpp
#include <atomic>
std::atomic<bool> stop(false);
void worker() {
while (!stop.load()) {
// do work
}
}
主线程:
cpp
stop.store(true);
现在一定安全。
五、Java 类比
Java 写法:
java
volatile boolean stop = false;
或者:
java
AtomicBoolean stop = new AtomicBoolean(false);
对比:
| Java | C++ |
|---|---|
| volatile | std::atomic |
| AtomicInteger | std::atomic<int> |
区别:
C++ 的 atomic 更底层。
六、atomic 的核心特性
1️⃣ 原子性(Atomicity)
操作不会被中断。
例如:
cpp
std::atomic<int> counter(0);
counter++;
不会出现:
- 读
- 改
- 写
被其他线程打断。
2️⃣ 可见性(Visibility)
一个线程写入后:
其他线程一定能看到。
3️⃣ 顺序一致性(默认)
默认使用:
cpp
memory_order_seq_cst
保证全局一致顺序。
(我们暂时不深挖内存序,只讲工程层)
七、atomic vs mutex 本质区别
| 对比 | atomic | mutex |
|---|---|---|
| 适合 | 简单变量 | 复杂资源 |
| 是否阻塞 | 不阻塞 | 可能阻塞 |
| 是否保护容器 | ❌ | ✅ |
| 是否能替代锁 | ❌ | ❌ |
一句话:
atomic 管"状态"
mutex 管"资源"
八、示例:多线程计数对比
错误版本
cpp
int counter = 0;
void task() {
for (int i = 0; i < 100000; ++i) {
counter++;
}
}
结果不稳定。
mutex 版本
cpp
std::mutex mtx;
int counter = 0;
void task() {
for (int i = 0; i < 100000; ++i) {
std::lock_guard<std::mutex> lock(mtx);
counter++;
}
}
正确,但性能差。
atomic 版本
cpp
std::atomic<int> counter(0);
void task() {
for (int i = 0; i < 100000; ++i) {
counter++;
}
}
正确,且更高效。
九、重要误区:atomic ≠ 无锁系统
很多人会说:
那我全用 atomic 不就行了?
错误。
atomic 不能保护:
- vector
- map
- queue
- 多个变量组合状态
例如:
cpp
if (x > 0) {
x--;
}
即使 x 是 atomic,也可能出错。
因为:
判断 + 修改 不是一个原子事务。
十、工程级示例:停止线程
这是最常见使用场景。
cpp
#include <thread>
#include <atomic>
#include <iostream>
#include <chrono>
std::atomic<bool> running(true);
void worker() {
while (running.load()) {
std::cout << "Working..." << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
std::cout << "Stopped." << std::endl;
}
int main() {
std::thread t(worker);
std::this_thread::sleep_for(std::chrono::seconds(3));
running.store(false);
t.join();
}
这就是:
atomic 做状态控制
join 做生命周期管理
十一、atomic 的高级形态(点到为止)
atomic 还支持:
cpp
compare_exchange_strong
compare_exchange_weak
用于:
- 无锁算法
- CAS 自旋
但:
90% 工程开发用不到。
不要一上来就写 lock-free 队列。
十二、系统取向思维升级
现在你有四层结构:
1️⃣ 线程模型 ------ 谁共享
2️⃣ mutex ------ 保护资源
3️⃣ condition_variable ------ 线程协作
4️⃣ atomic ------ 状态一致性
十三、工程口诀
状态用 atomic
容器用 mutex
等待用 condition_variable
生命周期用 join
十四、和 Java 再对比
Java:
- synchronized = mutex
- wait/notify = condition_variable
- AtomicXXX / volatile = atomic
C++:
- 更底层
- 更危险
- 更灵活
十五、本篇总结一句话
atomic 不是锁的替代品
它是"轻量级状态同步工具"
下一篇(真正组合拳)
第六篇我们做:
工程实战 ------ 用 mutex + cv + atomic 搭一个任务系统骨架
- 可提交任务
- 可阻塞等待
- 可安全关闭
- 正确释放线程
这是你并发系统能力的第一块工程里程碑。