39、并发编程基础知识

并发编程 / 2021-01-10

1、多道技术基本概念

  多道指的是多个程序,即多个程序在操作系统的有序调度之下实现肉眼上的同时运行多个程序的现象,这便是多路复用!多路复用又分两种:

1.1、空间上的复用

  即多个程序共享计算机硬件资源,比如内存分成多份分别分配给不同的程序,它们彼此间相互隔离,犹自以为自己独占内存空间!

1.2、时间上的共用

  当某个程序在等待I/O过程中,无所事事,操作系统将其挂起转而调用CPU执行下一个程序!比如说我们敲一条命令去读取某个文件的内容,当CPU执行这条命令后,该程序需要等待从磁盘中读取文件内容到内存上,这段时间它仅仅占用磁盘I/O,而CPU处于闲置状态;那么基于多道技术,操作系统会趁这个空闲时间保存这个程序的状态,切换并运行下一个程序,等到文件内容读取完毕再切换至第一个程序,多个程序之间如此类推;又因计算机速度极快,在一刹那间可运行切换人类难以想象的数量的程序,这给我们造成了一种肉眼上的同时运行多个程序的假象!这也是并发的原理!它们并不是真正的同一时刻能够同时运行多个程序,只是肉眼看起来是而已!

  当然了,当一个程序长时间占用CPU时,操作系统也会剥夺它对CPU的执行权限,不然别的程序都不用运行了,全给它占完了!

1.3、并行与并发

  上面提到了并发不是并行,它只是看起来像是同时运行,因为它CPU只有一颗核心!那什么是并行?你一颗CPU核心不能真正同时运行多个程序,那我便拥有多个核心不就完了!当然了,多颗CPU核心,每一颗上面都可以实现单核+多道技术实现并发,这便是我们现在常见的动辄八核十六线程的原因!

2、程序与进程基础

2.1、程序与进程的区别

  程序就是一堆躺在硬盘的代码的集合体,它是静态的;进程则是程序正处于执行的过程,它是动态的!

  举个例子,程序就是一位身怀绝世武功的侠客!无论他有多么惊世骇俗的武功,当他睡觉的时候没有处于运功状态,他就是一个普通人,静静地躺在床上;当他开始练功或者与人打斗时,运功周天,挥手间惊天动地的招式便使将来,此时动态的他便是无敌的侠客!同理,程序是指令+数据的集合体,代码写出来只有执行了才有用处;侠客也是人+武功的集合体,只有他运起功来才算是武功有了用武之地!

2.2、进程调度

  既然操作系统有进程调度,那就涉及到调度算法!这里简单提一下有哪些即可,深究过于底层的技术知识无益于我们初学者!

2.2.1、先来先得

  即先执行者优先分配CPU资源;这对耗时较长的任务有利,而对耗时较短的任务无利!很容易理解,耗时长的一旦执行起来,后面的即使只需要1毫秒即可也轮不到它们运行!

2.2.2、短作业优先

优先执行耗时较短的任务而不管你耗时长的是不是先执行

2.2.3、时间片轮转法+多级反馈队列

  时间片轮转法指的是操作系统将CPU资源分配成一个个的时间片,每个进程分配若干个时间片!举个例子来帮助理解,1秒=1000毫秒,以1毫秒为一个时间片段来进行分配,分配到的时间片决定了每个进程可以得到CPU多长时间的执行权限,过了就把你踢掉切换至下一个进程!有点像当年读书,每个人给你这么多年的时间去读书学习,等到毕业后不管你愿不愿意,你便不再是少年时候的学生了!

  回过神来,分配完时间片后程序开始按照多道技术来排队等待执行,任务队列也分层级的;当队列中出现新任务,CPU会暂停当前任务,执行新第一层队列中的任务,而被暂停的任务则被挤到后面去,耗时较长的进程消耗完时间片后又找操作系统要钱要资源,操作系统反复给了几次后发现这货贪得无厌,还没运行完还要索取,于是乎将其冷落,打到最后的队列去!这便是多级反馈机制的简单阐述!

2.3、进程状态

进程从开始到结束有三种状态,它们分别是:就绪、运行、阻塞

就绪

  比如说将程序从磁盘(存储设备)上读取代码到内存完毕后,程序开始准备执行,这时候称之为就绪状态!

运行

  CPU正在执行程序的过程,这时候程序换了一种身份叫进程;此时的它便处于运行状态!

阻塞

  当程序产生I/O时,比如执行到某一段代码时需要读写文件而产生磁盘I/O;这时候它需要等待结果返回,操作系统会将其挂起,想动也动不了!此时的它,便处于阻塞状态!

3、同步/异步/阻塞/非阻塞

3.1、同步/异步

指的是任务的提交方式

同步

  提交后,原地等待任务返回结果,等待过程中不能做任何事,即我们人所说的干等,看起来程序就像是卡住了一样!

异步

  提交后,不原地等待结果,先去做其它事,等待任务返回结果自动返回给提交者,但是任务的结果如何获取?Python有一个异步回调机制自动处理任务的返回结果,我们毋须过于关注!

3.2、阻塞/非阻塞

指的是程序的运行状态

阻塞

即进程处于阻塞状态,常见于产生I/O操作,比如磁盘I/O、网络I/O等!

非阻塞

即进程处于就绪态、运行态;就算没有立即得到结果也会立即返回,而不会阻塞当前进程的进行!

3.3、同步≠阻塞

很多人会把阻塞和同步混为一谈,其实不尽然!同步等待结果的时候,它大部分时候处于就绪状态!因为它等待的那个结果不一定会产生I/O,既然没有I/O,何来的阻塞呢?下面的例子可以说是同步非阻塞:

def print_str():
    print('hello')


def test():
    print('你好')
    print_str()
    

test()
# 执行得到结果
你好
hello

所以我们编程的理想是程序永远处于异步非阻塞,代码永远处于就绪态和运行态之间切换,效率简直上天了!

世间微尘里 独爱茶酒中