atomic 头文件

 

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()
//不细说了,以后碰到有机会来补充吧