并发编程理论之多道技术、进程

并发编程理论之多道技术、进程

操作系统的发展

  1. 穿孔卡片

    1946年第一台计算机诞生--20世纪50年代中期,还未出现操作系统,计算机工作采用手工操作方式。

    手工操作

    程序员将对应用程序和数据的已穿孔的纸带(或卡片)装入输入机,然后启动输入机把程序和数据输入计算机内存,接着通过控制台开关启动程序针对数据运行;计算完毕,打印机输出计算结果;用户取走结果并卸下纸带(或卡片)后,才让下一个用户上机。

    在这个时期,用户独占全机,而CPU要等待手工操作,CPU利用极其不充分。

  2. 批处理系统

    主机和输入机之间增加了磁带这一存储设备,成批地把输入机上的用户作业读入磁带,而计算机依次把磁带上的用户作业读入主机内存并执行并把计算结果向输出机输出。

    相比手工操作,效率还是提升很多的,但是CPU的性能过剩还是很严重。

    后来还引入了脱机批处理系统,增加一台不与主机直接相连而专门用于与输入/输出设备打交道的卫星机。当然起到的提升也是巨大,因为CPU不必反复等待程序录入了。

    但是CPU的利用率依然有很大的提升空间。

  3. 多道处理技术

    允许多个程序同时进入内存并运行

    假设有两个程序想要在某cpu中运行,传统的单道程序工作程序占用时间的方式如下

    img

    而多道程序的占用时间方式如下:

    img

    实际上,就是IO操作不用CPU来做,所以在程序有IO请求时,我们的CPU就转去做其他程序的计算工作,而避免等待原程序IO操作而CPU可以进行其他工作的时间。

    这样原本需要T1+T2才能结束的两段程序,使用多道技术后就可以不用这么长时间了。

多道技术实现方式

  1. 切换

    计算机在程序遇到IO操作或者程序长时间占用CPU时会将CPU的使用权交给其他程序。

  2. 保存状态

    在把CPU使用权交接出去时,会保存当前程序的执行状态保存下来,使已经执行过的程序是有效的。

进程理论

进程是一个具有独立功能的程序关于某个数据集合的一次运行活动。它可以申请和拥有系统资源,是一个动态的概念,是一个活动的实体。

简单来说,是正在运行中的程序,其占用着某些包括CPU在内的系统资源。

刚才在介绍正在跑的程序A和程序B就是进程A和进程B。

进程的并行与并发

  • 并行

    多个进程同时执行,须要多个cpu配合,同时运行这多个进程

  • 并发

    多个进程在某一时段看起来同时运行了,但实际上每一时刻只有一个进程在被CPU执行

我们刚才所介绍的多道技术就是提现了并发的思想。

进程的调度算法

进程的调度算法指操作系统面对多个进程时,如何分配CPU时间的策略。

  1. 先来先服务的算法

    简单的策略,哪个进程先进来,就先执行完哪个进程

    缺点很明显,当遇到一个长时间进程时,对短进程不友好

  2. 短作业优先调度法

    也很粗暴,哪个进程执行的快就执行哪个进程,对长进程又不友好

  3. 时间片轮转法

    每次分配CPU的一个小时间段给某个进程,到时间就保存当前进程状态和切换另外一个进程。

    这样分配,所有程序都不会等太久。

  4. 多级反馈队列

    在时间片轮转的基础上,这个程序如果每次都是因为时间片到时间了而不是IO而被剥夺使用权,那就每次分配长一点的时间片给这个程序。

    这样可以减少轮转的频率,也可以让各种程序都执行的相对连贯。

进程的三种状态

  • 就绪态:每当程序开始运行编程进程,都会进入这个状态,是等待CPU的使用权的状态

  • 运行态:正被CPU执行的状态

    • 当程序结束会进入终止态
    • 当程序遇到IO会进入运行态
    • 当时间片到会返回就绪态(cpu要分配给其他进程)
  • 阻塞态:正在执行IO操作时,这是CPU就不会去选择执行这段进程

    当阻塞态结束会重新回到就绪态。

image

  • 创建态和终止态:进程开始和结束的状态

同步与异步

在多进程并发中,我们认为,所有的进程都是有异步属性的,即当一个进程遇到IO等操作,这个进程不会进入一个忙等待的状态(占用CPU却又等待IO的返回值)而会直接交出CPU的执行权给其他进程,让CPU去执行其他的任务。

而同步就是指要等待IO或者其他任务的结果进程才会继续向前,类似于一个接一个的串行执行,达成多个任务的在某种节点上的同步。

简单来表述:

  • 同步就是提交完任务后原地等待任务的返回结果
  • 异步就是提交完任务后就转去做其他的任务,等结果出来了,任务再重新处于就绪的状态,继续推进

阻塞与非阻塞

阻塞就是进程进入阻塞态,非阻塞就是程序处于就绪态和运行态。

当一个任务因调用或IO要获取某个结果,阻塞调用必须等待调用的结果,非阻塞调用则可以不必等待这个结果而使进程始终处于就绪态和运行态。(这又牵扯到线程的概念)

异步非阻塞

当我们说一个程序是异步非阻塞的程序,实际上就是说这个程序很吃cpu的利用率,同时也效率最高。因为,这样的程序高并发,且多个进程始终处于就绪态和运行态,所以CPU执行这个程序中的进程的时间就会比较多,执行速度就会很快,这种方式常应用于游戏行业。

进程通信和进程数据管理

进程通信

进程之间的数据默认是隔离的,每个进程间的数据变量都存在于各自的名称空间中,不会互相影响。

我们可以通过消息队列和文件操作等方式实现进程间的通信,让不同的进程能够对同一份数据进行操作。

但是这样又可能出现数据错乱的问题,比如说:

进程A与进程B都想要对{"num":1}进行减一操作,
因为进程之间是异步的,所以两者有可能同时拿到了num的值
并赋予到其自己名称空间的某个变量上,然后个进程又同时将
自己的变量减一后存入了num中。
显然原本应该被减两次的num应该是-1
最终存入的是却只被减过一次的0

这就是异步的进程对同一份数据操作而产生的数据错乱

互斥操作

而为了防止数据错乱的情况,我们应该将操作数据变成一种同步的状态,即当一个进程A操作数据时,另一个进程B就不应该操作这份数据了,而应该等着当前进程A处理完数据进程B再去操作这个数据。这样可以让我们的数据变得安全。

lock.acquire()  # 进入区
读取文件中的num并-1然后存储  # 临界区
lock.release()  # 退出区
  • 进入区:所有需要操作数据的进程,都需要到这个区来等待数据的操作权

    ​ 当判断没有进程进入临界区时,就取最先进入的进程进入临界区
    ​ 当判断已经有进程在临界区内,就要在进入区等待

  • 临界区:同时只有一个进程可以进入临界区做一些处理数据的操作

  • 退出区:是进程处理完数据,释放互斥锁的过程

posted @ 2022-11-17 19:24  leethon  阅读(93)  评论(0编辑  收藏  举报