多线程的发展史可以说是一场资源效率之战,促进其发展的根本动力其实在于磁盘IO、内存IO、CPU运算速度的不平衡所造成的资源浪费,其中CPU运行速度最快、其次是内存、最慢的当属磁盘IO;如果说CPU的运转速度是火箭,那么我们的内存IO速度就是一辆小汽车,当然我们的磁盘IO速度会更慢只能相当于我们走路。
而我们计算机每一个工作中通常包含它们两个以上的角色参与,当他们一起协作的时候就产生问题了,CPU执行速度非常快,所以它的大部分时间都是在等内存和磁盘的IO完成工作,因为CPU的效率要远远高过磁盘IO,CPU资源又是非常宝贵的,所以就必须想一种办法把CPU在等待磁盘IO的这段时间利用起来。
单任务时代
真空管和穿孔打卡
最初的计算机并没有我们现在使用的计算机这么厉害,最开始计算机只负责计算的部分,它并不能直接写入指令和输出结果。
当时的工作流程是由程序员首先把程序写到纸上,然后穿孔成卡片,再把卡片输入到计算机上,计算机计算得到结果打印出来,程序员最后拿到打印出来的结果。
然而在当时计算机的资源可算是相当昂贵的,因为计算机计算速度很快,我们会发现时间都花在了把程序编程传卡片,和把卡片交给计算机处理的过程中,而这个过程中计算机大部分时间都处于等待指令输入的闲置状态,从而使造成计算机资源浪费。
晶体管和批处理
为了更有效的利用计算机资源,不让它处于闲置状态,所以出现了批处理,批处理是对指令的输入上做了优化,之前只能通过把指令输入成一个小卡片然后交给计算机执行,而批处理是可以一下把非常多的指令录入一个磁带内,这样减少了指令从打印到交给计算机读取的过程,然后计算机把计算结果输出到一个磁带内,整个过程好比之前我们喝水是每次渴了的时候就拿水杯从池塘去打杯水喝,而现在是你每次渴了的时候就拿两个桶去挑一担一样,挑回来的水每次都够喝一段时间,节省了你很多次去大打水的过程,这个磁带就相当于我们的水桶,然后我们只需要不间断的交给计算磁带,计算机就资源就更好的利用了起来。
多进程时代
集成电路和多道程序设计
批处理虽然提高了计算机整体资源利用的问题,但是这个时代的计算机还只能开启一个任务,一台计算器同时只能干一件事情,进行读取或写入的时候就不能进行计算,进行计算的时候就不能进行读写的IO。因为IO和CPU计算速度存在着巨大的差异,这样就造成了CPU所花的计算时间非常少,而大部分的时间都在等待IO的结束。
所以为了更合理的利用CPU资源,我们就把内存划分为多个块,不同的应用程序使用着各自的内存空间互不干涉,这里单独的应用程序也就单独分为了一个进程,CPU可以在多个进程之间切换这执行,当一个进程需要进行磁盘IO的时候CPU就切换到另外一个进程去执行指令,这样就让CPU的资源更合理的运用起来,这个时候随着内存的加大,可划分的块也越来越多,这样能“同时”运行的进程也越来越多,CPU在不同的进程之间切换执行,任务多的时候它会一直处于工作状态。
当然随着CPU工作模式的变换,同时也衍生了一些概念。
CPU时间片
所以为了公平的照顾到每个进程,每一次切换进程后,它都会只会工作一段时间,然后又切换到另外一个进程任执行一段时间,而我们把CPU切换进程任务的这个时间段叫做“时间片”。
程序计数器
因为CPU切换任务后,下次回来需要知道当前任务已经执行到了哪一步,所以对任务节点进行一个标记,回来的时候可以从上次标记的位置继续工作,而这个标记叫做程序计数器。
堆栈指针
每个进程分配的内存是独立的,所以切换进程任务回来后我们需要知道当前进程分配的内存地址是那一块,而这个地址就是堆栈指针。
多线程时代
CPU基于进程的调度极大的提高了CPU的利用率,但是为了精益求精,我们发现基于进程的调度还是有改善的空间。因为CPU是基于进程切换的,一个进程工作时那么就意味着其他进程无法获得CPU资源。这样就好比我们在写word文档的时候就不能同时听着酷狗音乐了。 另外当一个进程任务执行一个IO时间较长的指令时,那些与IO无关系的操作也必须等待IO执行完才能执行。
所以我们的CPU选择了基于粒度更小的线程来调度执行任务。一个进程可以创建很多个线程来执行任务,没有了进程的界限区分,CPU会在线程之间来回切换的工作, CPU的时间片分的更细,以至他在多个线程之间切换执行,我们并没有明显的感知,这样的话我们多个进程也变得可以“同时”工作了,同时CPU等待IO的几率又更加小了。
多核CPU
随着应用程序越来越丰富,我们的计算机承担的工作也越来越多,之前我们一直怕CPU的资源利用不上,但是随着计算机软件程序的快速发展,我们反而感觉到CPU不够用了,
所以我们就想着既然一个CPU不够用,那么我们是不是可以多加几个CPU来弥补呢,所以就出现了现在的双核、四核、八核.....甚至更多核心控制器的CPU。
之前我们只有一个CPU,所以在某一个时间片之内永远只会有一个线程执行指令,因为时间片的单位太小以至于我们感知不到,以为多个程序的线程是“同时”执行的,但是进入了多核CPU时代后,多核心控制器可以同一时间执行不通过线程上的任务,从而真正意义上实现了多任务同时执行。
CPU缓存
还是老问题,CPU觉得每次去内存里面读写数据太慢了,为了更快的读写数据,所以CPU在自己家里加了一层缓存,每次都是先从自己的缓存读写数据,这样CPU的利用率又提高了,但是正因为每个CPU都有了自己的缓存,而它各个缓存又是互相不可见的,所以又衍生了一些问题。
总结
因为CPU、内存、磁盘IO 存在着巨大的速度差异,所以计算机经过了单任务、多进程、多线程、多核CPU、CPU缓存 这些技术更新过程,这些技术更新最终目的都是为了弥补CPU、内存、磁盘IO速度上的差距,更有效的利用起CPU资源,经过了这些后我们的CPU利用率和运算效率确实都得到了非常大的提升,但是也为后来的人遗留了一些程序并发上面的问题。
因为CPU可以切换线程执行指令所以会导致我们的一些操作会失去原子性,因为CPU缓存会导致可见问题、还有CPU的一些指令重排序优化也会导致一些问题,关于这些我们将在并发问题的三大根源一文中继续探讨。