进程、线程 和 协程
进程和线程都是对CPU工作时间片的描述。
基本概念
程序
- 程序:一个在时间上按严格次序、顺序执行的操作序列。
- 操作:数据处理的一种规则,一经启动就需要在有限时间内完成。
- 计算:若干操作严格顺序执行的集合。
- 在计算机系统中只有一个程序在运行,这个程序独占系统中所有资源,执行不受外界影响。通常一个程序可分为若干程序段,它们必须按照某种先后次序执行,仅当前操作执行后,才能执行后继操作。
程序的顺序执行
一个具有独立功能的程序独占处理机,直至得到最终结果的过程。
程序顺序执行的特点:
- 顺序性:当顺序程序在处理机上执行时,处理机的操作是严格按照程序所规定的顺序执行的。
- 封闭性:程序一旦开始执行,计算结果不受外界影响。
- 可再现性:程序执行的结果与它的执行速度无关,只与初始条件有关。
程序的并发执行
若干程序段同时在系统中运行,这些程序段的执行在时间上是重叠的,一个程序段的执行尚未结束,另一个程序段的执行已经开始。
程序的并发执行的特点:
- 间断性:一个程序的执行可能被其他程序的执行打断
- 失去封闭性:如果一个程序的执行可以改变另一个程序的变量,那么后者的输出就可能依赖前一个程序执行的相对速度,失去程序的封闭性。
- 程序与计算不再一一对应:程序是指令的有序集合,是静态的;计算是指令序列在处理机上的执行过程,是动态的。当多个计算任务共享某个程序时,它们都可以调用这个程序,且调用一次就是执行一次计算。
- 相互制约:当并发执行的各程序之间需要协同操作来完成一个共同的任务是它们之间具有直接的相互制约关系。
进程
进程是一个程序及其数据在处理机上顺序执行时所发生的活动,它是系统进行资源分配和调度的一个独立单位。
进程的属性:
- 资源的拥有者:给每个进程分配一个虚拟地址空间,保存进程映像,控制一些资源(文件,I/O设备),有状态和优先级。
进程的状态- 运行态:该时刻进程实际占用CPU。
- 就绪态:可运行,但因为其他进程正在运行而暂时停止。
- 阻塞态:除非某种外部事件发生,否则进程不能运行。
- 执行/调度单位:进程是一个执行轨迹,是处理器调度的基本单元。
进程的组成:程序+数据+进程控制块PCB
进程控制块:系统为了管理进程设置的一个专门的数据结构,存放了用于描述该进程情况和控制进程运行所需的全部信息。系统利用PCB来控制和管理进程,所以PCB是系统感知进程存在的唯一标志。进程与PCB是一一对应的。
进程控制块PCB的组织方式
- 链接方式:按照进程状态将PCB分为多个队列。操作系统持有指向各个队列的指针。
- 索引方式:根据进程状态的不同建立多个索引表。
进程的特点:
- 动态性:进程的实质是程序在多道程序系统中的一次执行过程,进程是动态产生,动态消亡的。
- 并发性:任何进程都可以和其他进程一起并发执行
- 独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位。
- 异步性:由于进程间的先相互制约,使进程具有执行的间断性,即进程按各自独立的,不可预知的速度向前推进。
原语与进程管理
- 原语:一种特殊的系统调用,它可以完成一个特定的功能,一般为外层软件所调用,其特点是原语执行时不可中断。原语具有原子性,即它是不可再分的。在操作系统中,原语是作为一个基本单位出现的。
- 进程管理:进程控制是对系统中所有进程从产生、存在到消亡的全过程实行有效的管理和控制。进程控制一般是由操作系统的内核来实现,内核在执行操作时,往往是通过执行各种原语操作来实现的。
- 进程创建:从无到有。
- 进程撤销:从存在到消亡
- 进程阻塞:由运行变阻塞
- 进程唤醒:由阻塞变就绪
- 进程延迟:延迟一段时间再执行
进程的创建:
- 系统初始化
- 正在运行的程序执行了创建进程的系统调用
- 用户请求创建一个新进程
- 一个批处理作业的初始化 进程的终止:
- 正常退出(自愿的)
- 进程中的错误(自愿的)
- 严重错误(非自愿的)
- 被其他进程杀死(非自愿)
进程与程序的区别
- 进程更能准确刻画并发,而程序不能
- 程序是静态的,进程是动态的
- 进程有生命周期,有诞生有消亡,是短暂的;而程序是相对长久的
- 一个程序可对应多个进程
- 进程具有创建其他进程的功能,而程序没有
线程
线程是进程中的一条执行路径,是进程的一个实体,可作为系统独立调度和分派的基本单位。
线程的特点:
- 不拥有系统资源(只拥有从属进程的全部资源,资源是分配给进程)
- 一个进程中的多个线程可并发执行。(进程可创建线程执行同一程序的不同部分)
- 系统开销小、切换快。(进程的多个线程都在进程的地址空间活动) 缺点:
- 因为进程资源共享,所以会产生资源竞争,需要通过锁机制来协同
- 当进程中的一个线程崩溃时,会导致其所属进程的所有线程崩溃(一般游戏的用户设计不会采用多线程方式)
线程的状态
如果系统支持线程的创建和线程的活动,那么处理机调度的最小单位是线程而不是进程。此时线程是争夺CPU的单位。 线程也有从创建到消亡的生命过程,在这个过程中它具有运行、等待、就绪或终止状态。
为什么引入线程?
对进程系统必须完成的操作:创建进程、撤销进程、进程切换。进程的引入提高了计算机资源的利用效率,但在进一步提高进程的并发性时,人们发现进程切换开销占的比重越来越大,同时进程间通信效率也受到限制。
- 问题:时空开销大,限制并发度的提高
- 解决:将资源分组处理和执行分离,使线程成为调度单位,只拥有必要资源不占有系统资源。
- 好处:
- 线程比进程更轻量级,创建和结束一个线程花费的时间少
- 两个线程切换花费时间少
- 因为同一进程内的线程共享内存和文件,因此它们之间相互通信无须调用内核
- 适合多处理机系统
线程的实现
- 用户态:只使用机器指令中的一个子集
- 核心态:操作系统具有对所有硬件的完全访问,可以执行机器能够运行的任何指令。
用户级线程(ULT)
与内核无关(切换速度特别快,不利用系统调用)
由TCB(Thread Control Block)控制管理
- 由应用程序完成所有线程的管理
- 通过线程库(用户空间) 管理
- 核心不知道线程的存在
- 线程切换不需要核心态特权
- 调度是应用特定的 用户级线程的核心活动:核心不知道线程的活动,但仍然管理线程的进程的活动。当线程调用系统调用时,整个进程阻塞。但对线程库来说,线程仍然是运行状态,即线程状态是与进程状态独立的。 优点:
- 线程切换不需要陷入内核,使得线程调度非常快捷。
- 允许每个进程有自己定制的调度算法 缺点: 单独的进程内部,没有时钟中断,不能用轮转调度,增加了算法设计复杂度。
核心级线程(KLT)
依赖于内核。(创建、撤消、切换由内核实现)
- 所有线程管理由核心完成
- 没有线程库,但对核心线程工具提供API
- 核心维护进程和线程的上下文
- 线程之间的切换需要核心支持
- 以线程为基础进行调度 优点:
- 对多处理器,核心可以同时调度同一进程的多个线程
- 阻塞是在线程一级完成
- 核心例程是多线程的 缺点: 在同一进程内的线程切换调用内核,导致速度下降
两者结合
使用内核级线程,将用户级线程和某些或者全部内核线程多路复用起来。
- 线程创建在用户空间完成
- 大量线程调度和同步在用户空间完成
- 程序员可以调整KLT的数量,可以取两者中最好的
进程与线程的关系
进程是任务调度的单位,也是系统资源的分配单位。线程是进程中的一条执行路径,当系统支持多线程处理时,线程是任务调度的单位,但不是系统资源分配的单位。
多线程还是多进程?
没有关系的任务建立多个进程;多个任务实际完成同一个作业,并彼此积极密切合作的情形中,应该建立包含多个线程的进程。
协程
协程,英文Coroutines,是一种基于线程之上,但又比线程更加轻量级的存在,这种由程序员自己写程序来管理的轻量级线程叫做『用户空间线程』,具有对内核来说不可见的特性。一个线程可以拥有多个协程。
协程的特点
- 线程的切换由操作系统负责调度,协程由用户自己进行调度,因此减少了上下文切换,提高了效率。
- 线程的默认Stack大小是1M,而协程更轻量,接近1K。因此可以在相同的内存中开启更多的协程。
- 由于在同一个线程上,因此可以避免竞争关系而使用锁。
- 适用于被阻塞的,且需要大量并发的场景。但不适用于大量计算的多线程,遇到此种情况,更好实用线程去解决。
协程的原理
当出现IO阻塞的时候,由协程的调度器进行调度,通过将数据流立刻yield掉(主动让出),并且记录当前栈上的数据,阻塞完后立刻再通过线程恢复栈,并把阻塞的结果放到这个线程上去跑,这样看上去好像跟写同步代码没有任何差别,这整个流程可以称为coroutine,而跑在由coroutine
负责调度的线程称为Fiber
。比如Golang里的 go关键字其实就是负责开启一个Fiber
,让func
逻辑跑在上面。
由于协程的暂停完全由程序控制,发生在用户态上;而线程的阻塞状态是由操作系统内核来进行切换,发生在内核态上。 因此,协程的开销远远小于线程的开销,也就没有了ContextSwitch上的开销。
协程和线程的比较
比较项 | 线程 | 协程 |
---|---|---|
占用资源 | 初始单位为1MB,固定不可变 | 初始一般为 2KB,可随需要而增大 |
调度所属 | 由 OS 的内核完成 | 由用户完成 |
切换开销 | 涉及模式切换(从用户态切换到内核态)、16个寄存器、PC、SP...等寄存器的刷新等 | 只有三个寄存器的值修改 - PC / SP / DX. |
性能问题 | 资源占用太高,频繁创建销毁会带来严重的性能问题 | 资源占用小,不会带来严重的性能问题 |
数据同步 | 需要用锁等机制确保数据的一直性和可见性 | 不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。 |
参考资料
- 操作系统学习笔记----进程/线程模型 - micro虾米 - 博客园 (cnblogs.com)
- 进程、线程与协程傻傻分不清?一文带你吃透! - SegmentFault 思否
- 一文读懂什么是进程、线程、协程 - 云+社区 - 腾讯云 (tencent.com)
上一篇: 漫谈进程和线程
下一篇: 速懂指南:进程、线程与协程详解