Skip to content

Latest commit

 

History

History
178 lines (98 loc) · 10.1 KB

02.线程管理.md

File metadata and controls

178 lines (98 loc) · 10.1 KB

02.线程管理

目录

背景

​ CPU+RAM+各种资源(比如显卡,光驱,键盘,GPS, 等等外设)构成我们的电脑,但是电脑的运行,实际就是CPU和相关寄存器以及RAM之间的事情。

一个最最基础的事实:CPU太快,太快,太快了,寄存器仅仅能够追的上他的脚步,RAM和别的挂在各总线上的设备则难以望其项背。那当多个任务要执行的时候怎么办呢?轮流着来?或者谁优先级高谁来?不管怎么样的策略,一句话就是在CPU看来就是轮流着来。而且因为速度差异,CPU实际的执行时间和等待执行的时间是数量级的差异。比如工作1秒钟,休息一个月。所以多个任务,轮流着来,让CPU不那么无聊,给流逝的时间增加再多一点点的意义。这些任务,在外在表现上就仿佛是同时在执行。

一个必须知道的事实:执行一段程序代码,实现一个功能的过程之前 ,当得到CPU的时候,相关的资源必须也已经就位,就是万事俱备只欠CPU这个东风。所有这些任务都处于就绪队列,然后由操作系统的调度算法,选出某个任务,让CPU来执行。然后就是PC指针指向该任务的代码开始,由CPU开始取指令,然后执行。

​ 这里要引入一个概念:除了CPU以外所有的执行环境,主要是寄存器的一些内容,就构成了的进程的上下文环境。进程的上下文是进程执行的环境。当这个程序执行完了,或者分配给他的CPU时间片用完了,那它就要被切换出去,等待下一次CPU的临幸。在被切换出去做的主要工作就是保存程序上下文,因为这个是下次他被CPU临幸的运行环境,必须保存。

串联起来的事实:前面讲过在CPU看来所有的任务都是一个一个的轮流执行的,具体的轮流方法就是:先加载进程A的上下文,然后开始执行A,保存进程A的上下文,调入下一个要执行的进程B的进程上下文,然后开始执行B,保存进程B的上下。

什么是线程

​ 在早期操作系统都是以 进程 为独立运行的基本单位,直到后面,计算机科学家又提出了更小的能独立运行的基本单位,它就是线程。

在现代操作系统,进程是最小的资源分配单位,线程是最小的运行单位,一个进程下面能有一个或多个线程,每个线程都有独立一套的寄存器和栈,这样可以确保线程的控制流是相对独立的。

​ 这里A、B、C线程共享A进程的上下文,CPU在执行的时候仅仅切换线程的上下文,而没有进行进程上下文切换的。进程的上下文切换的时间开销是远远大于线程上下文时间的开销。这样就让CPU的有效使用率得到提高。

在同一个进程里的多个线程是共享资源的,同一个进程内的各线程共享进程的**代码区,数据区,堆,每个线程在进程的栈空间创建一个属于自己的栈空间。

​ 与进程相比,创建线程所需要的时间将更少,占用的资源、通信的成本也比较少,所以线程被称之为轻量级进程。多线程的存在将增强系统的吞吐量,增加单位时间内的作业处理量。

线程是操作系统调度和执行的最小单位

线程拥有自己独立的栈。

一个进程中可以有多个线程,它们共享进程资源。

进程和线程都是一个时间段的描述,是CPU工作时间段的描述,不过是颗粒大小不同。

QQ 和浏览器是两个进程,浏览器进程里面有很多线程,例如 HTTP 请求线程、事件响应线程、渲染线程等等,线程的并发执行使得在浏览器中点击一个新链接从而发起 HTTP 请求时,浏览器还可以响应用户的其它事件。


线程的上下文切换

当进程只有一个线程时,可以认为进程等于线程,线程上下文的切换分两种情况

  1. 不同进程的线程,切换的过程就跟进程上下文切换一样
  2. 两个线程是属于同一个进程,因为虚拟内存是共享的,所以在切换时,虚拟内存这些资源就保持不动,只需要切换线程的私有数据、寄存器等不共享的数据

所以线程的上下文切换相比进程,开销要小很多

线程分类

操作系统中,线程有以下三种分类

  • 内核线程:在内核空间就实现的线程,由内核管理
  • 用户线程:在用户空间实现的线程,不归内核管理,是由用户态通过线程库完成线程的管理(用户态是指线程或进程在用户空间运行)
  • 轻量级进程:在内核中来支持用户线程(用户线程与内核线程的中间层,内核线程的高度抽象)
内核线程

​ 因为内核线程是由内核空间管理,所以它的 结构线程控制块(Thread Control Block, TCB) 在内核空间,操作系统对 TCB 是可见的

img

内核线程有什么优点

  • 内核线程的由内核空间管理,线程的创建、销毁、调度等,都不用你操心,全自动化,属于智能型
  • 内核线程能利用cpu多核的特性,实现并行执行(因为由内核管理,非常智能)
  • 内核线程阻塞,不会影响其他内核线程(因为由内核管理,非常智能)

内核线程有什么缺点

  • 因为是内核管理,所以内核线程的大部分操作都涉及到内核态,即需要从用户态切换到内核态,开销较大
  • 因为内核资源有限,所以无法大量创建内核线程
用户线程

​ 因为 用户线程 在用户空间,是由 用户态 通过线程库来管理,所以它的 结构线程控制块(Thread Control Block, TCB) 是在线程库里面,对于操作系统而言是看不到 T CB 的,它只能看到整个进程的 PCB(内核无法管理用户线程,也感知不到用户线程)

img

用户线程有什么优点

  • 因为用户线程创建、销毁、调度等都不走内核态,直接在用户态进行操作,所以速度特别快
  • 不依赖内核,可用于不支持线程技术的操作系统
  • 可以大量创建用户线程,不消耗内核资源

用户线程有什么缺点

  • 用户线程创建、销毁、调度等需要自己实现相应线程库
  • 用户线程阻塞会导致整个进程内的其他用户线程阻塞(整个进程阻塞),因为内核感知不到用户线程,所以无法去调度其他用户线程
  • 无法利用cpu多核特性,还是因为内核感知不到用户线程
轻量级进程(Light-weight process,LWP)

​ 轻量级进程(Light-weight process,LWP)可以理解成内核线程的高级抽象,一个 进程 可以有一个或多个LWP ,因为每个 LWP 与 内核线程 一对一映射,所以 LWP 都是由一个 内核线程 支持(用户线程关联LWP,即成为内核支持的用户线程)。

​ 在大多数系统中,LWP与 普通进程 的区别也在于它只有一个最小的执行上下文和调度程序所需的统计信息。一般来说,一个进程 代表程序的一个实例,而 LWP 代表程序的执行线程,因为一个 执行线程 不像进程那样需要那么多状态信息,所以 LWP 也不带有这样的信息。

总结

​ 每个线程共享进程的代码段内存空间,所以我们编写多线程代码的时候,可以在任何线程调用任何函数。

​ 每个线程共享进程的数据段内存空间,所以我们编写多线程代码的时候,可以在任何线程访问全局变量。

​ 每个线程共享进程的堆,所以我们编写多线程代码的时候,可以在一个线程访问另外一个线程new/malloc出来的内存对象。

​ 每个线程都有自己的栈的空间,所以可以独立调用执行函数(参数,局部变量,函数跳转)相互之间不受影响。

做个简单的比喻:进程=火车,线程=车厢

  • 线程在进程下行进(单纯的车厢无法运行)
  • 一个进程可以包含多个线程(一辆火车可以有多个车厢)
  • 不同进程间数据很难共享(一辆火车上的乘客很难换到另外一辆火车,比如站点换乘)
  • 同一进程下不同线程间数据很易共享(A车厢换到B车厢很容易)
  • 进程要比线程消耗更多的计算机资源(采用多列火车相比多个车厢更耗资源)
  • 进程间不会相互影响,一个线程挂掉将导致整个进程挂掉(一列火车不会影响到另外一列火车,但是如果一列火车上中间的一节车厢着火了,将影响到所有车厢)
  • 进程可以拓展到多机,进程最多适合多核(不同火车可以开在多个轨道上,同一火车的车厢不能在行进的不同的轨道上)
  • 进程使用的内存地址可以上锁,即一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。(比如火车上的洗手间)-"互斥锁"
  • 进程使用的内存地址可以限定使用量(比如火车上的餐厅,最多只允许多少人进入,如果满了需要在门口等,等有人出来了才能进去)-“信号量”

参考链接