【C++八股】std::atomic —— 原子操作
std::atomic
是 C++11 引入的模板类,主要用于多线程编程中的原子操作,确保在多个线程访问或修改共享变量时不会产生数据竞争。
1. std::atomic
的作用
在多线程环境下,普通变量的操作不是原子的,可能被多个线程同时访问和修改,导致数据竞争。
示例(非原子操作可能导致错误):
#include <iostream>
#include <thread>
int counter = 0; // 非原子变量
void increment() {
for (int i = 0; i < 100000; ++i) {
++counter; // 可能导致数据竞争
}
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
std::cout << "Counter: " << counter << std::endl; // 可能得到不确定的结果
}
这里 ++counter
不是原子操作,两个线程可能会同时读取 counter
,然后都执行 ++
,导致最终结果错误。
解决方案:使用 std::atomic
#include <iostream>
#include <thread>
#include <atomic>
std::atomic<int> counter(0); // 使用 std::atomic
void increment() {
for (int i = 0; i < 100000; ++i) {
++counter; // 线程安全的操作
}
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
std::cout << "Counter: " << counter << std::endl; // 结果始终正确
}
✅ std::atomic<int>
确保 counter
的所有操作都是原子的,消除了数据竞争。
2. std::atomic
的核心特点
- 保证原子性:所有操作(如
++
、--
、赋值等)都是不可分割的,不会被其他线程中断。 - 无锁实现:通常使用硬件级的原子指令(如
lock cmpxchg
),比互斥锁std::mutex
开销更低。 - 支持基本类型:如
int
、bool
、float
、pointer
等,也支持自定义类型(但要求是TriviallyCopyable
)。 - 支持原子操作:
fetch_add()
/fetch_sub()
(类似++
和--
)compare_exchange_strong()
/compare_exchange_weak()
(CAS 操作)load()
/store()
(获取和设置值)
3. std::atomic
vs std::mutex
std::atomic
适用于简单的数值或指针变量,能够提供高效的原子操作。std::mutex
适用于更复杂的多线程同步需求,比如保护多个变量或复杂数据结构。
总结
✅ std::atomic
适用于多线程环境,提供无锁原子操作,避免数据竞争。
✅ 比 std::mutex
更高效,但只适用于简单变量(int、指针等)。
✅ 支持 CAS(比较交换)、fetch_add、fetch_sub 等常见原子操作。
✅ 对于复杂数据结构(如链表、队列),推荐使用 std::mutex
或 std::shared_mutex
。
重点
-
std::atomic
是什么?- 是 C++11 引入的无锁原子操作,适用于多线程共享变量。
-
和
std::mutex
的区别?std::atomic
无锁,开销低,适用于简单变量。std::mutex
需要锁,开销高,适用于复杂数据结构。
-
compare_exchange_strong()
的作用?- CAS 操作,仅当预期值相等时才更新,适用于无锁数据结构。
-
什么时候用
std::atomic_flag
?- 适用于自旋锁,比
std::mutex
更轻量,但可能导致 CPU 忙等。
- 适用于自旋锁,比
📢 总结:如果你想提高多线程性能,优先考虑 std::atomic
,但如果涉及多个变量或复杂数据结构,还是用 std::mutex
更安全! 🚀