关于多线程的一切:原子操作

本文深入探讨了原子操作的概念,强调其在多线程编程中的重要性。原子操作保证了任务的不可分割性,防止线程间的数据竞争。文中详细介绍了原子指令,如Store/Load和Read-Modify-Write,并讨论了不同架构下的原子性保证。同时,文章还讲解了软件层面的原子操作,包括操作系统、编译器和编程语言提供的接口。原子操作是实现无锁编程的基础,但在Lock-free编程中,还需要结合Memory Barrier来确保正确性。
摘要由CSDN通过智能技术生成

原子,意味着不可切分的最小单元,程序中的原子操作指任务不可切分到更小的步骤。

原子性(atomic)是一个可见性的概念:

当我们称一个操作是atomic的,实际上隐含了一个对什么atomic的上下文。

注意:我们说的是从线程视角观察不到完成一半的状态,而并非不存在物理上的进度状态,它取决于你的观察视角。

比如说一个线程中被互斥锁保护的区域,对另一个线程是atomic的,因为从另一个线程视角来看,它没法进入临界区读到数据中间状态,但是对kernel而言却不是atomic的。

从线程视角只能观察到未做和已做两种状态,观察不到完成一半的状态,任务执行不会被中断,也不会穿插进其他操作。

原子性对多线程操作是一个非常重要的属性,因为它不可切分,所以,一个线程没法在另一个线程执行原子操作的时候穿插进去。

比如一个线程原子的写入共享数据,那么其他线程没有办法读到“半修改的数据”;同样,如果一个线程原子读取共享数据,那么它读取的是共享变量在那个瞬间的值,因此原子的读和写没有数据竞争(Data Race)。

原子操作常用于与顺序无关的场景。

原子指令

原子指令指单一的不可再分的不可中断的被硬件直接执行的机器指令,原子指令是无锁编程的基石。

原子指令常被分成两类:

  • store/load
  • read-modify-write(RMW)

Store/Load指令

  • store:存储数据到内存,对应变量写(赋值)
  • load:从内存加载数据,对应变量读

通常,一条简单的store/load机器指令是原子的,比如数据复制指令(mov)可以把内存位置的数据读取到CPU寄存器,相当于Load数据。

x86架构读/写“按数据类型对齐要求对齐的长度不大于机器字长的数据”是原子的。

那什么是数据类型对齐要求呢?

比如在x86_64架构LLP64系统上(LLP64指long、long long和pointer类型是64位的),只要int32类型数据满足放置在起始地址除4为0,int64/long类型数据满足起始地址除8为0,则该数据就是满足类型对齐要求,那么对它的读和写,都是原子的。

一字节的数据读写一定是原子的。

其实,Intel新CPU架构确保读写放置在一个Cache Line的数据(不大于机器字长),跨Cache Line的数据访问无法保证原子性。

C/C++编程中,变量和结构体会自动满足对齐要求,比如:

int i;

void f() {
   long y;
}

struct Foo {
   int x;
   short s;
   void* ptr;
} foo;

全局变量i会被放置在起始地址可以被4整除的内存位置,局部变量y会被放置在起始地址可以被8

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值