临界面积
说到并发编程,我们首先想到的是临界区的概念,临界区是线程中的一段代码,访问关键资源,需要互斥执行。
临界资源
关键资源是指线程间共享的资源,但不同执行顺序的结果是不确定的,也称为竞态条件。
编程基本逻辑封装原子操作->:信号量->:实现临界区->:管程
确保同一时间只有一个人获得密钥(原子性)
多线程的三个特点:原子性、可见性、顺序性(详见本文:多线程的三个特点)
如果只能一个人在家,进去就锁,出来就开;没有锁的人只能在外面等。如何保证同一时间只有一个线程进入临界区,此时需要保证线程的原子性。
禁用硬件中断:我们知道,系统调用和执行流程的切换依赖于软中断。中断禁用后,进程(线程)不会被切换出去,从而保证代码段可以完成执行。但是,缺点也很明显。因为中断被禁用,如果关键区域代码一直执行,其他进程就没有机会执行了。再者,只能禁止单个CPU的中断。
基于软件同步:也就是说,同步互斥是基于代码实现的,而peterson算法更适合解决两个进程互斥访问关键区域的问题。详细参考:实现临界区互斥的算法和方法的演进
基于原子操作原语的方法:常见的原子操作指令包括测试和设置、比较和交换。
示例:比较和交换
CAS的缩写,compare和swap,从中文翻译为compare和swap,在intel CPU中使用cmpxchg指令。
一个CAS操作包含三个操作数——内存位置(v)、预期的原始值(a)和新值(b)。如果存储器位置的值与预期的原始值匹配,处理器将自动将位置值更新为新值。否则,处理器什么也不做。在任一种情况下,它都将在CAS指令之前返回该位置的值。(在CAS的一些特殊情况下,只返回CAS是否成功,不提取当前值。)CAS有效声明“我认为位置V应该包含值A;如果包括这个值,就把b放在这个位置;否则,不要换位置,直接告诉我位置的当前值。
许多编程语言锁定机制都是基于cas封装的,如下图所示。
关于CAS更详细的参考:CAS详细说明和应用。
话说回来,计算机如何保证这些指令的原子性?继续挖,又会出现一个?
CPU缓存模型
我们知道,CPU与物理内存的通信速度比CPU的处理速度慢很多,所以CPU有自己的内部缓存,按照一定的规则,将内存中的数据读入内部缓存,以加快频繁读取。我们假设一台PC上只有一个CPU和一个内部缓存,那么所有进程和线程看到的数字就是缓存中的数字,不会有问题;但是服务器通常是多CPU的,更一般的是每个CPU都有多个内核,每个内核都维护自己的缓存,所以多线程并发时会出现缓存不一致的情况,会导致严重的问题。
带缓存的CPU执行计算的流程
程序以及数据被加载到主内存指令和数据被加载到CPU的高速缓存CPU执行指令,把结果写到高速缓存高速缓存中的数据写回主内存。更多细节请参考:cpu模型和JMM模型,jvm内存模型
处理器如何实现原子操作
32位IA-32处理器采用基于锁定缓存或总线的方式实现多处理器间的原子操作。
1处理器自动保证基本内存操作的原子性
首先,处理器会自动保证基本内存操作的原子性。处理器保证从系统内存中读取或写入一个字节是原子的,这意味着当一个处理器读取一个字节时,其他处理器不能访问该字节的内存地址。奔腾6和最新的处理器可以自动保证单个处理器在同一个缓存行中的16/32/64位操作是原子的,但是复杂的内存操作处理器不能自动保证其原子性,比如跨总线宽度的访问,多缓存行,页表。然而,处理器提供总线锁定和缓存锁定机制,以确保复杂内存操作的原子性。
2使用总线锁来确保原子性
第一种机制是通过总线锁保证原子性。为了确保读取和重写共享变量的操作是原子的,有必要确保当CPU1读取和重写共享变量时,CPU2不能操作缓存共享变量的内存地址的缓存。
处理器使用总线锁来解决这个问题。所谓总线锁就是使用处理器提供的lock #信号。当一个处理器在总线上输出这个信号时,其他处理器的请求将被阻塞,因此处理器可以独占使用共享内存。
3使用缓存锁来确保原子性(缓存一致性协议MESI)
这种机制通过缓存锁定来保证原子性。同时我们只需要保证对某个内存地址的操作是原子性的,但是总线锁锁定了CPU和内存之间的通信,使得其他处理器在锁定期间无法操作其他内存地址的数据,所以总线锁的开销比较大。最近处理器在某些场合使用缓存锁代替总线锁进行优化。
同时,我们只需要保证对某个内存地址的操作是原子性的,经常使用的内存会缓存在处理器的L1、L2、L3缓存中,所以原子性的操作可以直接在处理器的内部缓存中进行,不需要声明总线锁。所谓“缓存锁”(cache Lock)是指如果内存区域缓存在处理器的缓存行中,并且在锁操作过程中被锁定(锁定缓存行并不锁定总线),那么当它执行锁操作并写回内存时,处理器不在总线上发送LOCK#信号,而是修改内部内存地址,并允许其缓存一致性机制保证操作的原子性,因为缓存一致性机制会防止两个以上处理器缓存的内存区域的数据同时被修改。当其他处理器写回并被锁定时,
但是,有两种情况下处理器不使用缓存锁定。第一种情况是:当操作的数据无法缓存在处理器中,或者操作的数据跨越多条缓存线时,处理器会调用总线锁。在第二种情况下,一些处理器不支持缓存锁定。对于Inter486和Pentium处理器,即使锁定的内存区域在处理器的高速缓存行中,也会调用总线锁。
以上两种机制可以通过Inter处理器提供的带有大量LOCK前缀的指令来实现。例如位测试和修改指令BTS,BTR,BTC,交换指令XADD,CMPXCHG等操作数和逻辑指令,如ADD (Add),OR (OR)等。,这些指令操作的内存区域将被锁定,导致其他处理器无法同时访问它。
高速缓存一致性协议MESI
MESI协议以高速缓存行的几种状态命名(高速缓存的基本数据单元,在英特尔中央处理器上通常为64字节)(全名为修改、独占、共享或无效)。该协议要求在每条高速缓存线上保持两个状态位,使得每个数据单元可以处于M、E、S和I四种状态之一,并且每个状态的含义如下:
M:被修改的。处于这一状态的数据,只在本CPU中有缓存数据,而其他CPU中没有。同时其状态相对于内存中的值来说,是已经被修改的,且没有更新到内存中。E:独占的。处于这一状态的数据,只有在本CPU中有缓存,且其数据没有修改,即与内存中一致。S:共享的。处于这一状态的数据在多个CPU中都有缓存,且与内存一致。I:无效的。本CPU中的这份缓存已经无效。有关详细信息,请参考文章:缓存一致性协议MESI
参考
https://www.cnblogs.com/kkkkkk/p/5543799.html
https://blog.csdn.net/liangwenmail/article/details/80832580
https://blog.csdn.net/java1993666/article/details/77880651
https://www.cnblogs.com/yanlong300/p/8986041.html
https://blog . csdn . net/weixin _ 41835916/article/details/80601373
https://blog.csdn.net/vincent1007/article/details/53999136
https://blog.csdn.net/u014634338/article/details/76092710
1.《线程同步 线程同步机制之底层原子实现方式》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。
2.《线程同步 线程同步机制之底层原子实现方式》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。
3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/caijing/735348.html