『面试问答』:原子操作是如何实现的?

面试官: 请说一下,原子操作是如何实现的?

现代计算机系统中,通过使用高速缓存cache进行数据交互,解决了CPU和内存之间速度的矛盾。在这种多核系统中,每个处理器都有自己的Cache,但它们共享同一主存,当多个 CPU 操作同一份数据时,各个 CPU 缓存中同一份数据的值可能会存在不一致的情况,也就是存在缓存一致性的问题。

比如多个线程对某个共享变量进行加操作,因为这种操作不是原子的,操作完之后,共享变量的值就会与期望值不一致。

原子操作表示操作是一个整体,不可再分。在并发场景下,如果无法保证某些操作是原子的,数据就无法一致,导致系统不可用。

CPU实现原子操作的方式一般有两种:

1、总线锁定

总线锁定是指处理器提供一个LOCK#信号,当一个处理器在总线上输出此信号时,其他处理器的请求将被阻塞,那么这个处理器就可以独占这个共享内存。这个锁的是CPU与内存之间的通信,就等于将其他处理器与内存的所有交互都锁住了。

在同一时刻,其实我们只需保证对某个内存地址的操作是原子性即可,但总线锁定把CPU和内存之间的通信锁住了,这使得锁定期间,其他处理器不能操作其他内存地址的数据,所以总线锁定的开销会比较大。

2、缓存锁定

频繁使用的内存会缓存在处理器的高速缓存里,那么原子操作就可以直接在处理器内部的缓存中进行,而并不需要声明总线锁。如果内存区域被缓存在处理器的缓存行中,并且在Lock操作期间被锁定,那么当它执行锁操作回写到内存时,处理器不在总线上输出LOCK#信号,而是修改内部的内存地址,并允许它的缓存一致性机制来保证操作的原子性,因为缓存一致性机制会阻止同时修改由两个以上处理器缓存的内存区域数据,当其他处理器回写已被锁定的缓存行数据时,会使缓存行无效。

虽然缓存锁定开销更小,但是有两种情况,处理器不会使用缓存锁定。 一种是当操作的数据不能被缓存在处理器内部,或操作的数据跨多个缓存行时,则处理器会调用总线锁定。 另一种是有些处理器不支持缓存锁定,就算锁定的内存区域在缓存行中,也会调用总线锁定。

Intel处理器提供了很多 LOCK 前缀的指令来实现总线锁定和缓存锁定。比如位测试和修改指令 BTS,BTR,BTC,交换指令 XADD,CMPXCHG等,被这些指令操作的内存区域就会加锁,导致其他处理器不能同时访问它。比如Java中实现原子操作的CAS(比较并交换)就是基于处理器提供的 CMPXCHG 指令来实现的。

软件开发面试问答 文章被收录于专栏

分享软件开发岗位面试题及答案

全部评论

相关推荐

1 收藏 评论
分享
牛客网
牛客企业服务