目录
1. 程序并发面临的3大问题
可见性、原子性、有序性(指令优化、指令重排)
原文地址:
https://zhuanlan.zhihu.com/p/64988344
2. 学习笔记和总结
1、为缓解cpu和io速度不匹配,提高cpu的利用率分别从
缓存、任务切换、指令排序
等多个方面进行优化
2、内存区域划分、时间片、进程
为了提高CPU的利用率,操作系统就有了进程和时间片
起初计算机内存中只允许运行一个程序,此时cpu大都是闲置的(因为要等待IO)。于是为了能同时运行多个程序,等待IO时运行其他任务,就出现了进程和线程。
内存划出不同的区域,分别由多个进程管理。cpu把自己的时间切分成若干片段(时间片),进程执行完一个时间片,就切换为另外的进程来执行(因此我们才可以一边操作word文档,一边用QQ聊天)。按时间片分配给不同的任务来执行。
3、(并发问题根源一):CPU切换线程执行导致的“原子性”问题
cpu切换任务(即线程)执行,导致了原子性问题。原子性就指是把一个操作或者多个操作视为一个整体,在执行的过程不能被中断的特性叫原子性。
进程共享同一区域的数据,后来cpu任务切花进行了优化,由进程变为了线程,但正是这种CPU可以在不同线程中切换执行的方式会使得我们程序执行的过程中产生原行性问题(因为多个线程之间是共享同一片内存区域的)
示例
number=number+1的指令可能如下:
指令1:CPU把number从内存拷贝到CPU缓存。
指令2:把number进行+1的操作。
指令3:把number回写到内存。
4、高速缓存的产生
CPU高速缓存是用于减少处理器访问内存所需的时间
5、(并发问题根源二):高速缓存导致的 “可见性” 问题
多核cpu时,每个cpu的缓存是独立不可见的,这时会造成不可见问题。
单核cpu时,不存在不可见的问题。 多个线程可能在不同的cpu上执行,各自操作各自的告诉缓存,因此会因不可见造成问题。
示例:
两个线程同时调用addNumber() 方法对number属性进行+1 ,循环10W次,等两个线程执行结束后,我们的预期结果number的值应该是20000,可是我们在多核CPU的环境下执行结果并非我们预期的值。
6、指令顺序优化(指令重排序)
发生在编译、CPU指令执行、缓存优化几个阶段,其“优化原则”就是只要能保证重排序后不影响单线程的运行结果,那么就允许指令重排序的发生。 其重排序的“大体逻辑”就是优先把CPU比较耗时的指令放到最先执行,然后在这些指令执行的空余时间来执行其他指令。
举例说明:就像我们做菜的时候会把熟的最慢的菜最先开始煮,然后在这个菜熟的时间段去做其它的菜,通过这种方式减少CPU的等待,更好的利用CPU的资源。
7、(并发问题根源之三):指令优化导致的重排序问题
小节:
进程和线程,本质是增加并行的任务数量来提升CPU利用率,缓存是通过把IO时间减少来提升CPU的利用率。
指令顺序优化的初衷的初衷就是想通过“调整CPU指令的执行顺序”和“异步化”的操作来提升CPU执行指令任务的效率。