Atomic 与 Mutex 在处理简单变量时的差异
1. 引言
在多线程编程中,保护共享变量的并发安全是核心问题。对于简单变量(如整数、布尔值、指针等),开发者通常有两种选择:使用原子操作(std::atomic)或互斥锁(std::mutex)。两者都能保证数据一致性,但在性能、使用方式、适用场景上存在显著差异。本文将从多个维度对比二者的区别,帮助你在实际开发中做出合理选择。
2. 基本概念
2.1 什么是 Atomic(原子操作)
原子操作是指不可分割的操作,即该操作要么完整执行,要么完全不执行,不会出现中间状态。std::atomic 是 C++11 引入的模板类,它利用 CPU 提供的原子指令(如 LOCK 前缀指令)直接对内存进行操作,无需操作系统介入。
#include <atomic>
std::atomic<int> counter{0};
// 原子自增
counter.fetch_add(1);
2.2 什么是 Mutex(互斥锁)
互斥锁是一种同步原语,通过加锁(lock)和解锁(unlock)来保护临界区。同一时刻只允许一个线程持有锁,其他线程必须等待。std::mutex 是 C++11 提供的标准互斥锁实现。
#include <mutex>
int counter = 0;
std::mutex mtx;
// 加锁保护
{
std::lock_guard<std::mutex> lock(mtx);
++counter;
}
3. 核心差异对比
3.1 性能差异
| 维度 | Atomic | Mutex |
|---|---|---|
| 开销来源 | CPU 原子指令,无系统调用 | 可能涉及系统调用与线程阻塞/唤醒 |
| 无竞争时 | 极快,仅几条指令 | 较快,但仍有锁获取/释放开销 |
| 高竞争时 | 可能自旋等待,占用 CPU | 线程可能休眠,让出 CPU |
| 适用规模 | 轻量级高频操作 | 重量级低频操作 |
结论:在低竞争场景下,atomic 通常比 mutex 快一个数量级以上;但在高竞争场景下,atomic 的自旋等待可能造成 CPU 空转,此时 mutex 的线程休眠机制反而更优。
3.2 功能差异
| 能力 | Atomic | Mutex |
|---|---|---|
| 保护单个变量 | ✅ 直接支持 | ✅ 支持 |
| 保护多个变量/复杂逻辑 | ❌ 不支持 | ✅ 支持 |
| 读改写复合操作 | ✅ 提供 fetch_add、compare_exchange 等 | ✅ 需手动加锁 |
| 条件等待 | ❌ 不支持 | ✅ 配合 condition_variable |
| 内存序控制 | ✅ 可指定 memory_order | ❌ 不可控(默认全序) |
3.3 使用复杂度
- Atomic:API 简洁,无需管理锁的生命周期,不存在死锁风险。
- Mutex:需要小心处理加锁/解锁配对,容易因异常或忘记解锁导致死锁;但
RAII封装(如lock_guard)可降低风险。
4. 典型场景示例
4.1 计数器累加
// Atomic 方式
std::atomic<int> count{0};
void increment() {
count.fetch_add(1, std::memory_order_relaxed);
}
// Mutex 方式
int count = 0;
std::mutex mtx;
void increment() {
std::lock_guard<std::mutex> lock(mtx);
++count;
}
4.2 共享标志位
// Atomic 方式
std::atomic<bool> ready{false};
// 线程 A
ready.store(true);
// 线程 B
if (ready.load()) { /* 执行 */ }
// Mutex 方式
bool ready = false;
std::mutex mtx;
// 线程 A
{
std::lock_guard<std::mutex> lock(mtx);
ready = true;
}
// 线程 B
{
std::lock_guard<std::mutex> lock(mtx);
if (ready) { /* 执行 */ }
}
5. 选择建议
| 场景 | 推荐方案 |
|---|---|
| 单个简单变量的高频读写 | std::atomic |
| 多个变量需要保持一致状态 | std::mutex |
| 需要条件变量等待通知 | std::mutex + condition_variable |
| 需要自定义内存序优化 | std::atomic |
| 临界区包含复杂逻辑或 I/O | std::mutex |
6. 总结
atomic 和 mutex 都能保证简单变量的线程安全,但设计哲学不同:atomic 通过硬件指令实现无锁的细粒度保护,适合高频、轻量的单变量操作;mutex 通过锁机制提供粗粒度的临界区保护,适合多变量、复杂逻辑或需要阻塞等待的场景。实际开发中应根据竞争程度、操作复杂度和性能要求综合权衡,必要时可混合使用两者。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)