线程

做一个超额的线程

( 手记 ) 进程同步与进程通信

目录

进程的顺序性与并发性

  1. 进程的顺序性:
    进程按照程序指定的指令顺序执行,只有在前一个指令结束后才能继续下一个指令
    当进程按顺序执行时,它会有两个特性
    a. 封闭性
    进程执行的最终结果取决于进程自身,不受其执行速度等外部影响。
    b. 可再现性
    重复执行该进程,得到的结果是一样的。
  2. 进程的并发性:
    在多道程序设计系统中,进程可以并发执行,即一个进程的工作还没完成,另一个进程就可以开始工作,进程与进程之间轮流竞争中央处理器(CUP),轮到谁取决于进程调度策略.并发进程之间可能是无关的独立进程,也可能是交互的依赖进程.
    交互进程并发执行时,会破坏其”封闭性”、”可再现性”.

时间引起并发进程的错误

Example:
某交通部门开发了一个车辆自动计数系统, 分别有一个记录过往车辆的进程vehicleCount,和一个每隔一小时(整点)将vehicleCount的结果输出的进程printCount.

  1. vehicleCount : 每有一辆车通过 count += 1.
  2. printCount : 输出count值后 count = 0.
    现在它们俩并发执行。
    当整点时printCount进程占用处理器输出count结果时,刚好有一辆车通过(count还未清零),这时vehicleCount重新抢回处理器执行将count + 1后printCount
    进程继续执行将count = 0, 这时最后的结果就会丢失一辆汽车计数,如果在printCount进程输出期间,有多辆汽车路过,就会丢失多辆汽车计数,结果就会出现偏差

临界区与PV操作

  1. 临界区
    交互并发进程因为时间引起的错误主要原因是对共享资源的使用不受限制,当进程交叉使用了共享资源就会造成错误.为了解决这个问题,我们必须对共享资源的使用加以限制.
    我们把进程中与共享资源有关的程序称为临界区
    在上个车辆的例子中,它们两个进程的临界区分别是
    count += 1;
    And
    print count;
    count = 0;
    保证一个进程在临界区执行时不允许另一个进程进入相关的临界区执行,各个进程对于共享资源的访问时互斥的,那么就不会造成时间引起的错误
    相关临界区是指并发进程中涉及相同变量的那些临界区
    对多个并发进程访问共享资源三个注意的地方:
    1. 一次最多一个进程进入临界区,其他进程等待
    2. 不能让一个进程无限制在临界区执行
    3. 不能让一个进程无限制的等待进入它的临界区
      实现上面的临界区管理,采用PV操作方式.
  2. PV操作
    由两个操作:P操作和V操作组成
    P(S),将信号量S减去1,若结果小于0,则把调用P(S)的进程设置为等待信号量S的状态
    V(S),将信号量S加1,若结果不大于0,则释放一个等待信号量S状态的进程
    如何管理临界区?
    S的初始值为1,那么第一个调用P(S)的进程成功进入临界区,此时S已经变成了0,那么后面的进程想进入临界区再调用P(S),S都会小于0,则让该进程变为等待信号量S的状态。
    一个进程在临界区执行完成结束后应调用V(S), 释放一个正在等待的进程进入临界区。

进程的互斥

  1. 读进程/写进程
    有多个进程都想对一个共享文件进行读写时,必须要进行适当的管理
    a. 不允许多进程同时访问共享文件
    通过PV操作,定义初始S为1,不管是读操作还是写操作都调用P(S)来竞争共享文件的使用权,结束后通过V(S)归还共享文件的使用权
    b. 允许多个进程同时访问共享文件
    进程需要遵循下面三条规则:
    1. 多个进程可以同时读共享文件.
    2. 任何一个进程对共享文件进行写(修改)时,不允许其他进程对该文件进行读或写.
    3. 当有进程在读共享文件时,不允许其他任何进程对该文件进行写.

进程的同步

Alt text

进程A读记录存到缓冲区,进程B从缓冲区调出记录进行处理。当进程A的执行速度大于进程B的时候就会发生错误.这个错误用进程互斥并不能解决,因为进程A和进程B虽然都共享一个缓冲区,但它们都是在缓冲区无进程使用的情况下进行读写。即进程A向缓冲区推送一堆数据,这个时候进程B还在处理上一批数据,在进程B还没处理完的情况下,进程A再次向缓冲区推送数据,引起数据覆盖错误.
我们可以使用互通消息的方法来控制进程的执行速度,使它们正确协调工作,具体要做:

  1. 进程A向缓冲区推送一堆数据的时候,应向进程B发送”缓冲区有待处理数据”的消息.
  2. 进程B向缓冲区取走一堆数据的时候,应向进程A发送”缓冲区的记录已被取走”的消息.
  3. 进程A只有得到进程B发来的消息时,才能继续向缓冲区推送数据。
  4. 进程B只有得到进程A发来的消息时,才能继续向缓冲区取数据,否则进程B一直等待.

用PV操作实现进程的同步(互通消息)

进程的同步是指并发进程之间相互制约,即一个进程依赖另一个进程传过来的消息,当一个进程没有得到另一个进程消息时应该等待,直到得到消息才被唤醒

  1. 同步机制
    实现进程的同步需要一种机制,可以把其他进程所需要的消息发出去,也能测试自己所需要的消息是否到达.
    用PV操作实现同步机制:
    a. 调用P操作测试消息是否到达.
    若消息未产生,则S等于0,这时进程调用P(S),应该让调用者处于等待信号量S的状态,直到消息到达,
    若消息已经存在,则S不等于0,这时进程调用P(S),调用者不会处于等待状态,可继续执行,即进程测试到需要的消息已经到达.
    b. 调用V操作发送信息
    进程需要向其他进程发信息时,可以调用V操作,若调用前S等于0,说明消息还未产生,且无等待消息的进程,这时调用V(S)时,V(S)使S信息量+1,则S已经不等于0,意味着消息已存在.
    若调用V(S)前,S小于0,则表示消息未产生前已有进程在等待该消息,调用V(S)释放一个等待进程,即表示被释放进程的等待消息已到达,可以继续执行.
  2. 同步和互斥的混合问题
    Example:
    某工厂有个存放设备的仓库,最多存放8台设备,生产部门生产的每台设备都要入库,销售部门每次从仓库提取设备出库供应客户。
    无论设备入库还是出库都需要运输工具,现在只有一套运输工具,每次只能运一台设备,设计能够协调工作的系统.
    设备的存放和提取可以用进程同步来解决,但这里多出了一个运输工具,则意味着出库和入库不仅要进程同步,还需要竞争运输工具,即共享资源有两个,一个是仓库,一个是运输工具
    定义三个变量:
    1. S1 : 仓库中尚可存放多少台设备,初始值为8
    2. S2 : 仓库中的设备数量,初始值为0
    3. Isleisure : 运输工具是否空闲,初始值为0,代表空闲
      生产部门生产一台设备准备入库,应调用P(S1)检查仓库是否可以存设备,调用P(Isleisure)检查运输工具是否空闲,如果上面两个条件都满足,则将一台设备入库,否则应等待.
      入库完成后应调用V(Isleisure)和V(S2)来归还运输工具的使用权和把仓库目前有设备的消息传给销售部门,销售部门收到消息时准备向仓库取设备,应先调用P(S2)检查仓库是否有设备,调用P(Isleisure)来检查运输工具是否空闲,如果条件都满足则从仓库取一台设备,否则等待. 取设备完成后应调用V(Isleisure)和V(S2)把设备工具使用权和仓库中尚可存放多少台设备的消息发送给生产部门,整个系统完成.

进程的通信

在上面进程同步和进程互斥中我们了解到并发进程要协调的访问共享资源(仓库,运输工具),进程与进程之间得交换消息. 通过PV操作可以实现进程同步和互斥的交换信息,所以PV操作也可以视为一种进程通信方式,但这种方式只交换了少量的信息,如(仓库尚可存放多少台设备,运输工具是否为空闲)等信息,只交换少量的信息的通信方式称为低级通信方式,有时进程间需要交换大量的信息,这种大量的信息得有专门的通信机制来实现,这是一种高级的通信方式,我们把通过专门的通信机制进程间交换大量信息的通信方式称为进程通信.

  1. 通信机制
    a. 信件
    一个进程要向其他进程发送信息时,应组织一封信
    信的内容包括:发送者名、信息(或信息存放的地址和长度)、等/不等回信、回信存放地址
    b. 通信方式
    两条原语,发送(send)和接受(receive)
    进程发送信件时,先组织一封信息,调用send原语,给出两个参数,一个是信件存放地址,一个是发到哪里.
    进程索取信件时,调用receive原语, 给出两个参数,一个是从哪里取信,一个是取到的信放在哪里.
    进程通信有直接通信和间接通信两种方式
    1. 直接通信方式
       send(B,M) 把信件M发送给进程B
       receive(A,X) 接受来自进程A的信件,把信件存入X中
    2. 间接通信方式
       send(N,M) 把信件M发送到信箱N
       reveive(N,X) 从信箱N中取出信存入X中
    

UNIX中的进程同步和进程通信

当进程在用户态执行时,调用wait和exit 实现进程同步. 当进程在核心态执行时,调用sleep和wakeup实现同步

  1. 用wait和exit实现同步
    fork父进程创建子进程后,调用wait让自己等待,等待期间,子进程按照父进程要求执行任务,任务执行完成后,调用exit来终止自己且释放父进程,父进程被释放后,继续执行后继操作.
  2. 用sleep和wakeup实现同步
    在核心态下,进程在执行时会遇到各种等待事件,无论遇到哪种等待事件,都调用sleep让进程进入睡眠,等待相关资源释放(或输入/输出操作结束,某个信号到达)时,重新用wakeup唤醒进程,使被唤醒的进程结束等待,而成为就绪状态.

UNIX中的进程通信

  1. 管道机制
    a. 无名管道
    实际就是连接进程间的一个共享文件,称为pipe文件,一个进程可以往pipe文件里写信息,另一个进程可以往pipe文件里读信息,pipe是一种临时文件,一旦信息被读取后,就没存在的必要,它占用的存储空间就会被回收.
    b. 命名管道
    命名管道也是一个文件,不过和无名管道不同,它冠有文件名,被删除前一直保存在文件系统,进程可以通过路径名来访问命名管道文件,对命名管道文件的使用方式就像对普通文件使用一样,要通过文件操作来使用,首先必须建立,读写之前先打开,通信结束后要关闭.
    命名管道文件的权限属于它的创建者,在建立命名管道文件的时候可以设置访问权限,只有被授权的用户才可以按访问权限访问命名管道文件,用命名管道文件实现通信时,发送者用”只写”方式打开,接收者用”只读”方式打开, 对于打开的命名管道文件,进程可按照打开的方式对文件进行读/写,通信结束时需要关闭该文件,以后需要的时候可以再次打开.
  2. 消息缓冲机制
    Unix中的消息缓冲机制实际是用缓冲区来传输信息,当一个进程要发送信息时,向系统申请一块缓冲区,然后把组织好的信息放进缓冲区里,接着把存有信息的缓冲区链接到消息队列里,接受消息的进程可以从消息队列中取信息,从缓冲区取出信息后,就应该释放该缓冲区
    在消息队列的每个缓冲区包含下面信息:
    a. 发送信息的进程名
    b. 消息长度
    c. 需传输的消息正文
    d. 下一个消息缓冲区指针

线程概念

线程是进程中可独立执行的子任务,一个进程可以有多个线程,每个线程都有一个唯一的标识符.
线程有如下属性:

  1. 每个线程都有一个唯一的标识符和一张线程描述表,线程描述表记录了线程执行时的寄存器和栈等现场状态.
  2. 不同的线程可以执行相同的程序
  3. 同一进程中的多个线程共享分配给该进程的主存地址空间
  4. 多个线程可以并发执行
  5. 一个线程被创建后,就开始了它的生命期,线程在生命期内会经历等待态、就绪态、运行态,直至请求完成终止,结束它的生命期

进程与线程 (多线程技术)

用线程处理生产者/消费者的问题 (多线程技术)
只需要一个进程,两个线程,一个生产线程,一个消费线程,他们共享进程的主存空间,也就不需要第三方的缓冲区(共享资源)。
多线程技术的优点:

  1. 创建线程无须分配额外资源,系统开销小.
  2. 线程间的通信在同一地址空间进行,不需要额外的通信机制,通信更方便,传输速度更快.
  3. 线程可以独立执行,能充分利用和发挥处理器和外围设备并行工作的能力.