atomic
1 概念
std::atomic 是C++ 11 引入的原子操作库,定义在
原子性在操作系统中有设计,执行过程,一旦开始不可被打断。指令在CPU执行层面被设计为不可中断。
2 内存屏障和内存顺序
内存屏障(Memory Barrier)是为了防止编译器和CPU对指令执行顺序进行优化重排。因为默认情况下,编译器和处理器会出于对性能优化目的,重排指令的执行顺序,可能会导致多线程环境下的内存一致性问题。
为了防止这一现象的出现,atomic提供了一种内存顺序控制。
| 内存顺序 | 功能 |
|---|---|
| memory_order_relaxed | 不做任何内存顺序控制,指令可以自由重排 |
| memory_order_acquire | 防止后续的读取指令重排到当前原子操作之前 |
| memory_order_release | 防止该原子操作之前的写操作重排到该原子操作之后 |
| memory_order_acq_rel | 结合acquire和release,确保原子操作前后的顺序性 |
| memory_order_seq_cst | 最严格的顺序控制,确保所有线成都按照一致的顺序执行原子操作 |
3. 无锁同步与自旋锁
原子操作常用于实现无锁同步(Lock-Free Synchronization)算法。自旋锁是其中的一种典型应用。
3.1 什么叫无锁同步?
无锁同步是一种在多线程环境下实现线成安全的技术,它的核心思想是不使用传统的互斥锁(mutex)来同步线程,而是使用原子操作确保共享数据的安全。
3.2 为什么引入无锁同步?
传统锁的缺陷
这里提及在操作系统中,我们经常遇到的问题。锁在多线程竞争可能会引发的问题。
- 死锁:最常见的问题,多个线程彼此等待对方释放锁,可能会造成互相阻塞,程序无法往下执行。
- 性能瓶颈:锁导致线程阻塞后,进而频繁上下文切换,严重影响程序的执行效率。
- 锁竞争:多个线程同一时间竞争同一把锁,某些线程会长时间阻塞。
无锁同步,引入原子操作,依赖CPU指令,它们在硬件层面保证不可中断,能确保线程安全,无需操作系统介入。
原子操作的原理:核心在于 CAS(Compare-And-Swap)或其他CPU指令(如LOCK_XADD、LOCK CMPXCHG),因为这些指令在单个CPU指令周期内完全修改,确保数据在多线程环境下不会被破坏。
3.3 无锁同步的类型
LOCK-FREE(锁自由)
即使有多个线程争夺同一资源,至少有一个线程能够成功执行,不会所有线程都陷入无限等待。
std::atomic<int> counter(0);
void increment() {
int experted = counter.load();
while (!counter.compare_exchange_weak(expected, expected + 1)) {
expect = counter.load();
}
}
4. std::atomic的常见操作
初始化和构造
std::atomic<T> a; //模板
std::atomic<int> counter(0); // 讲原子整数counter初始化为0
加载和存储
//load() 和 store()
int value = counter.load(); //原子的读取counter的值
counter.store(20); //原子地将counter 设置为20
加法和减法操作
counter.fetch_add(1); // 原子 + 1
counter.fetch_sub(1); // 原子 - 1
交换操作
int old-value = counter.exchange(10);
//原子地将 counter 的值变为10,并且返回旧的值
比较并交换(CAS)
compare_exchange_strong()
compare_exchange_weak()
//不细说了,以后碰到有机会来补充吧
PREVIOUSC++ 类型转化和拷贝
NEXTgames101(01-04)