并发的发展历史
真空管和穿孔打卡
由于早期的计算机只是解决一些简单的计算问题,而且生产水平也比较落后的情况下,往往处理一些计算之前,都需要实现准备材料。
程序员通过编写程序将内容写到纸上,接着打孔成卡片,再将卡片转移给操作室的操作员,并将卡片的内容输入到计算机上,等到计算机计算完成后,会将结果打印到打印机上进行输出,操作员拿到输出结果送到输入室,程序员就可以从输出室得到具体的计算结果。如此往复。
流程:写到纸上(人工)->打孔(机器)->输入到计算机(人工)->得到结果(机器)->输出室(人工)->从输出室获得结果(人工)。
优点:复杂的计算部分交与机器。节省人工。
缺点:我们可以发现,整个计算流程,计算机其实有太多空闲时间,这些空间时间基本都浪费在人工输出和输入到另一台机器上。
改进:引入批处理系统
晶体管和批处理系统
批处理系统首先将一个个的任务,组合成一个总任务,并将卡片的指令输入操作移交给了一台廉价的机器,具体做法是:我们将所有的任务都在输入室输入完成,接着用另外一台机器会将这些输入的磁带指令全部输入到计算机中,然后就是一条效率可观的流水线,计算机通过指令进行计算,然后将结果直接输出到磁带是上,再打印出结果。
流程:写到纸上(人工,全部任务)->输入到机器并且、计算、打印(机械)->打印机得到结果(人工搬运输出磁带并且放到打印机上输出)
优点:由于人工基本只需要输入一次,所以大大减少了机器的空闲时间,可以让计算机一直处于运行的状态,不浪费计算机资源。
缺点:既然大部分人工问题解决后,那么具体的效率问题自然落到了机器本身上,由于在读取资源的时候,可能会存在等待磁盘或者I/O阻塞一些其他问题,会导致某一个任务需要等待前一个任务阻塞完毕后,才可以接着开始他的工作,这就造成了严重的cpu资源浪费,如果I/O场景比较多的情况下,这种情况会更加惨烈。
改进:集成电路和多道程序设计
集成电路和多道程序设计
既然某个任务会因为硬件或者软件层面的问题,导致后续任务无法执行的话,那为什么不多开几个任务通道,而不是让满满的任务挤在一条狭隘的队列里呢?这就是多道程序设计的由来,多道程序设计将内存分成几个部分,每个部分放在不同的程序,当一个程序因为某些原因阻塞无法运行的时候,可以将cpu的使用权限切换给其它不阻塞的程序,那么总体来看,这台机器并没有因为某些原因阻塞而导致工作停滞不前。
如果内存中同时存放到足够多的程序,CPU利用率就会接近100%
流程:略
优点:不阻塞的执行任务
缺点:略
改进:略
对进程和线程的解释
进程
我们可以发现,在引入多道程序设计
的时候,已经可以实现某种意义上的并行
,即便这种实现的方法不够高端。在多道概念的被设计出来的时候,这种将内存分块并且赋予他们能够执行任务权限的概念,就有了一个名称——进程。
首先每个进程相互独立,他们拥有自己的内存地址空间,由于CPU在对进程进行时间片切换的时候,需要知道被切换的线程上一次运行的位置,并从那个位置开始执行。所以每个进程会有自己的程序计数器和堆栈指针。
目前为止,我们只针对单核cpu的情况,单核并行的情况无非是通过cpu的时间片切换来“看起来像是并行
”,同时,在任意一个时刻也只有一个进程被cpu调度,因为一个cpu只能被一个进程独占。
线程
有了进程为什么还需要线程呢?
很简单,因为创建线程的代价比进程更小,所以线程的创建、销毁比进程更加快,我们可以简单的认为,线程是轻量级的进程。
同时,在多核cpu中,这个时候的多线程才算是真正意义上的并行,为此我们需要代价更少的设计来减少创建执行的损耗。
再者,因为创建的代价更少,所以意味着我们可以为一个程序创建更多的线程来为他的任务服务。
例如,你有一个记事本的程序,每当你完成一篇文章,这时候有一个专门的线程来记录你写了些什么,如果这个时候断电了,你可能会失去这些数据,因为内存还没有写到磁盘中。那么我们是否可以再额外创建一个线程每隔10秒,来执行保存操作,这样就可以提升用户的体验感。
并且真正应用程序,会存在多个同时执行的任务,你肯定不希望因为某一个任务阻塞,而导致不依赖这个任务的其他任务也被阻塞,所以线程可以将进程细化,将任务拆分成更多的线程去驱动。即便阻塞也不会影响其他任务,因为此时在不同线程中,他们同样彼此独立,不受影响。
每天积累一点点