CPU 和缓存一致性
咱们应该都晓得,计算机在执行程序的时候,每条指令都是在 CPU 中执行的,而执行的时候,又免不了要和数据打交道。而计算机下面的数据,是寄存在主存当中的,也就是计算机的物理内存。
刚开始,还相安无事的,然而随着 CPU 技术的倒退,CPU 的执行速度越来越快。而因为内存的技术并没有太大的变动,所以从内存中读取和写入数据的过程和 CPU 的执行速度比起来差距就会越来越大, 这就导致 CPU 每次操作内存都要消耗很多等待时间。
可是,不能因为内存的读写速度慢,就不倒退 CPU 技术了吧,总不能让内存成为计算机解决的瓶颈吧。
所以,人们想进去了一个好的方法,就是在 CPU 和内存之间减少高速缓存。缓存的概念大家都晓得,就是保留一份数据拷贝。他的特点是速度快,内存小,并且低廉。
那么,程序的执行过程就变成了:
当程序在运行过程中,会将运算须要的数据从主存复制一份到 CPU 的高速缓存当中,那么 CPU 进行计算时就能够间接从它的高速缓存读取数据和向其中写入数据,当运算完结之后,再将高速缓存中的数据刷新到主存当中。
而随着 CPU 能力的一直晋升,一层缓存就缓缓的无奈满足要求了,就逐步的衍生出多级缓存。
依照数据读取程序和与 CPU 联合的严密水平,CPU 缓存能够分为一级缓存(L1),二级缓存(L2),局部高端 CPU 还具备三级缓存(L3),每一级缓存中所贮存的全副数据都是下一级缓存的一部分。
这三种缓存的技术难度和制作老本是绝对递加的,所以其容量也是绝对递增的。
那么,在有了多级缓存之后,程序的执行就变成了:
当 CPU 要读取一个数据时,首先从一级缓存中查找,如果没有找到再从二级缓存中查找,如果还是没有就从三级缓存或内存中查找。
单核 CPU 只含有一套 L1,L2,L3 缓存;
如果 CPU 含有多个外围,即多核 CPU,则每个外围都含有一套 L1(甚至和 L2)缓存,而共享 L3(或者和 L2)缓存。
随着计算机能力一直晋升,开始反对多线程。那么问题就来了。咱们别离来剖析下单线程、多线程在单核 CPU、多核 CPU 中的影响。
单线程。cpu 外围的缓存只被一个线程拜访。缓存独占,不会呈现拜访抵触等问题。
单核 CPU,多线程。过程中的多个线程会同时拜访过程中的共享数据,CPU 将某块内存加载到缓存后,不同线程在拜访雷同的物理地址的时候,都会映射到雷同的缓存地位,这样即便产生线程的切换,缓存依然不会生效。但因为任何时刻只能有一个线程在执行,因而不会呈现缓存拜访抵触。
多核 CPU,多线程。每个核都至多有一个 L1 缓存。多个线程拜访过程中的某个共享内存,且这多个线程别离在不同的外围上执行,则每个外围都会在各自的 caehe 中保留一份共享内存的缓冲。因为多核是能够并行的,可能会呈现多个线程同时写各自的缓存的状况,而各自的 cache 之间的数据就有可能不同。
在 CPU 和主存之间减少缓存,在多线程场景下就可能存在 缓存一致性问题,也就是说,在多核 CPU 中,每个核的本人的缓存中,对于同一个数据的缓存内容可能不统一。
处理器优化和指令重排
下面提到在在 CPU 和主存之间减少缓存,在多线程场景下会存在 缓存一致性问题 。除了这种状况,还有一种硬件问题也比拟重要。那就是为了使处理器外部的运算单元可能尽量的被充分利用,处理器可能会对输出代码进行乱序执行解决。这就是 处理器优化。
除了当初很多风行的处理器会对代码进行优化乱序解决,很多编程语言的编译器也会有相似的优化,比方 Java 虚拟机的即时编译器(JIT)也会做 指令重排。
可想而知,如果任由处理器优化和编译器对指令重排的话,就可能导致各种各样的问题。
并发编程的问题
原子性 是指在一个操作中就是 cpu 不能够在中途暂停而后再调度,既不被中断操作,要么执行实现,要么就不执行。
可见性 是指当多个线程拜访同一个变量时,一个线程批改了这个变量的值,其余线程可能立刻看失去批改的值。
有序性 即程序执行的程序依照代码的先后顺序执行。
缓存一致性问题 其实就是 可见性问题 。而 处理器优化 是能够导致 原子性问题 的。指令重排 即会导致 有序性问题。