一、原子变量、原子操作
- 锁竞争:互斥锁、条件变量、原子变量、信号量、读写锁、自旋锁。
- 在高性能基础组件优化的时候,为了进一步提高并发性能,可以使用原子变量。
- 性能:原子变量 > 自旋锁 > 互斥锁。
- 操作临界资源的时间较长时使用互斥锁(粒度大)。
- 如果只操作单个变量的话,可以使用原子变量(粒度小) 进行优化。
- 给基础类型或者指针加上一个标记
std::atomic<T>
之后,它就是原子变量,对这些变量的操作就是原子操作。
- 原子变量是一种多线程编程中常用的同步机制,它能确保对共享变量的操作在执行时不会被其它线程的操作干扰,从而避免竞态条件。
- 原子变量具备原子性,也就是要么全部完成,要么全部未完成。
- 为什么会使用到原子变量 ?
- 通常某一个变量对应的操作,其
CPU
的指令是大于 1
个指令的,在多线程环境下,可能会引发竞态,在这种线程不安全的情况下,我们需要为这个变量单独设置一个锁安全的标记 std::atomic<T>
,从而实现线程安全。
- 多线程环境下,确保对共享变量的操作在执行时不会被干扰,从而避免竞态条件。
std::atomic<T>
is_lock_free;
store(T desired, std::memory_order order);
load(std::memory_order order);
exchange(std::atomic<T>* obj, T desired);
compare_exchange_weak(T& expected, T val, memory_order success, memory_order failure);
compare_exchange_strong((T& expected, T val, memory_order success, memory_order failure);
fetch_add
fetch_sub
fetch_and
fetch_or
fetch_xor
二、原子性
- 要么都做要么还没做,不会让其它核心看到执行的一个中间状态。
- 单处理器单核心实现原子性:
- 只需要保证操作指令不被打断。
- 屏蔽中断 → 不允许操作指令被打断,不让其它线程看到操作指令的中间状态。
- 底层硬件自旋锁 → 为了实现线程切换。
- 多处理器或多核心实现原子性:
- 除了要保证操作指令不被打断,还需要避免其它核心操作相关的内存空间。
- 以往的
0x86
lock
指令,锁总线,避免所有内存的访问。
- 现在的
lock
指令,锁总线,只阻止其它核心对相关内存空间的访问,也就是只锁住相关内存空间。
- 内存总线:
CPU
需要通过内存总线访问内存。
- 磁盘总线:
CPU
需要通过磁盘总线访问磁盘。
三、CPU 缓存一致性工作原理
- 为什么要有
CPU
缓存:为了解决 CPU
运算速度与内存访问速度不匹配的问题,所以在 CPU
和内存之间设置了一些缓存。
CPU
和磁盘之间也有一个缓存 → 高速缓冲区(page cache
) → 为了解决 CPU
运算速度与磁盘访问速度不匹配的问题。
CPU
中有三种缓存:L1
和 L2
是核心独有的,L3
是核心共有的,也就是多个核心共用一个 L3
。离核心越近访问速度越快,存储容量越小。
CPU
缓存的基本单位:cache line
,64
字节。每次最少读取的数据空间(不管用户要读的数据空间有多小)。
flag
:标识缓存中的数据是否可用,存储的是缓存的状态值(MESI
)。
tag
:索引数据是否在缓存中、数据在缓存中的位置。
data
:具体存储的数据。

- 在
CPU
缓存的基础上,