• 操作系统复习:线程


    线程

    在传统操作系统中,每个进程有一个地址空间和一个控制线程。不过经常存在一个地址空间中准并行运行多个控制线程的情形,这些线程就像(差不多)分离的进程(共享地址空间除外)。

    2.2.1 线程的使用

    使用多线程的原因:

    • 程序设计模型:在许多应用中同时发生着多种活动。其中某些活动会随着时间的推移被阻塞。通过将这些应用程序分解成可以准并行运行的多个顺序线程,程序设计模型会变得简单。
    • 创建和撤销的开销:线程比进程更加轻量级,比进程更加容易创建,也更加容易撤销。在有大量线程需要动态和快速修改时,具有这一特性是非常重要的。
    • 性能考虑:若多个线程都是CPU密集型的,那么并不能获得性能的增强,但是如果存在大量的计算和大量的I/O处理,拥有多个线程允许这些活动彼此重叠进行,会加速程序执行的速度
    • 多线程使真正的并行成为可能[CH8]

    多线程模型中:并行实体有共享同一个地址空间和所有可用数据的能力。

    Example:文件编辑器的例子——三个线程能够代替三个进程

    image-20220901153232202

    2.2.2 经典的线程模型

    进程模型基于两种独立的概念:资源分组处理与执行。将这两种概念分开,就引入了“线程”的概念。

    2.2.2.1 理解进程的角度:资源分组

    理解进程的一个角度是:用某种方法将相关的资源集中在一起。

    进程有存放程序正文和数据以及其他资源的地址空间,这些资源中包含打开的文件、子进程、即将发生的定时器、信号处理程序、账号信息等。把他们都放到进程中更容易管理。

    2.2.2.2 理解进程的角度:线程

    进程拥有一个执行的线程,通常简写为线程。线程中有:

    • 一个程序计数器,用来记录接着要执行哪条指令
    • 寄存器,用来保存线程当前的工作变量
    • 堆栈,用来记录执行历史

    进程用于将资源集中在一起,线程是在CPU上被调度的实体。

    (1)多线程理解

    同一个进程中并行运行多个线程,是对在同一台计算机中并行运行多个进程的模拟。

    • 多个线程共享同一个地址空间和其他资源。
    • 多个进程共享物理内存,磁盘,打印机和其他资源

    在2-11a中,可以看到三个传统的进程。每个进程都有自己的地址空间和单个控制线程。每个线程在不同的地址空间中运行。

    在2-11b中,可以看到一个进程带有三个控制线程。每个线程在相同的地址空间中运行。

    image-20220901174345963

    进程中的线程,所有线程有相同的地址空间,共享同样的全局变量。一个线程可以读、写另一个线程的堆栈,甚至可以清除另一个线程的堆栈。线程之间是没有保护的

    image-20220901175549093
    (2)线程的状态

    线程的状态和进程一样:

    • 就绪
    • 运行
    • 阻塞
    (3)线程堆栈

    每个线程的堆栈有一帧,供各个被调用但是还没有从中返回的过程使用。在该栈帧中存放了相应过程的局部变量以及过程调用完成之后使用的返回地址。

    通常每个线程会调用不同的过程,从而有一个各自不同的执行历史,故每个线程都要有自己的堆栈。

    image-20220901194929053
    (4)线程的创建

    多线程的情况下,进程通常会从当前的单个线程开始。线程调用库函数(如thread_create)创建。thread_create()参数指定了新线程要运行的过程名。

    线程之间是平等的,不论有无层次关系,创建线程通常都会返回一个线程标识符,该标识符是新线程的名字。

    (5)线程的退出
    • thread_exit

    线程完成工作后,可以通过一个库过程(如thread_exit)退出,接着该线程消失,不再可调度。这种情况下,现成的创建和终止类似于进程的创建和终止。

    • thread_join

    某些线程调用中,例如thread_join,可以线程可以等待一个(特定)线程退出。这个过程堵塞调用直到那个特殊的线程退出。

    • thread_yeild

    允许线程自动放弃CPU从而让另一个线程运行。

    因为线程不同于进程,所以无法利用时钟中断强制线程让出CPU。

    2.2.3 POSIX线程

    IEEE定义了线程的标准。定义的线程包叫做pthread。大部分UNIX支持该标准。

    所有pthread线程都有某些特性。每一个都有一个标识符,一组寄存器和一组存储在结构中的属性。这些属性包括堆栈大小、调度参数以及其他线程需要的项目。

    image-20220901204049858
    • pthread_attr_init

      建立一个线程的属性结构,并初始化成默认值。

    • pthread_attr_destory

      删除一个线程的属性数据结构,释放它占用的内存。不会影响调用它的线程,这些线程会继续存在。

    2.2.4 线程的实现

    有两种主要的方式实现线程包:在用户空间和在内核中。这两种方法互有利弊,不过混合实现的方式也有可能。

    2.2.4.1 在用户空间中实现线程

    把整个线程包放在用户空间中,内核对线程包一无所知。从内核的角度考虑,就是按照正常的方式进行管理:单线程进程。线程在一个运行时系统的上层运行,该运行时系统是一个管理线程的过程的集合。如:pthread_createpthread_exitpthread_joinpthread_yeild等。

    image-20220901205640181

    在用户空间管理线程时,每个进程需要有其专用的线程表,用来跟踪该进程中的线程。线程表与进程表相似,不过仅仅记录各个线程的属性,如每个线程的程序计数器、堆栈指针、寄存器和状态等。线程表由运行时系统管理。

    在用户空间实现的优点:

    • 用户线程包可以在不支持线程的操作系统上实现
    • 允许每个进程有自己定制的调度算法
    • 线程切换至少比陷入内核要快一个数量级:thread_yeild后调用线程调度程序来选择另一个要运行的程序,调度是本地过程。

    在用户空间实现的缺点:

    • 无法实现阻塞系统调用,因为会阻塞所有的线程
    • 如果一个线程引起了缺页中断,那么整个进程都会被阻塞直到I/O完成,尽管有其他的线程可以执行
    • 如果一个线程开始运行,那么在该进程中的其他线程就不能运行,除非第一个线程自动放弃CPU
    2.2.4.2 在内核中实现线程

    在内核中实现线程,不再需要运行时系统,进程中也没有线程表。

    内核中有用来记录系统中所有线程的线程表。当某个线程希望创建一个新线程或者撤销一个已有的线程时,他进行一个系统调用,这个系统调用通过对线程表的更新完成线程的创建或者撤销。

    线程表维护的信息:

    • 寄存器
    • 状态
    • 其他信息
    image-20220901231001645

    与在用户空间中一样。另外,内核维护了传统的进程表,以跟踪进程的状态。

    优点:

    • 所有能够阻塞线程的调用都以系统调用的形式实现。当一个线程阻塞时,可以运行进程中的另一个线程。
    • 缺页中断类似

    缺点:

    但是内核中创建或者撤销线程的代价比较大

    2.2.4.3 混合实现

    引入内核级线程,然后将用户线程与全部内核线程多路复用起来。

    编程人员可以决定多少个内核级线程和多少个用户级线程彼此多路复用。带来了最大的灵活度。

    内核只识别内核级线程,并对其进行调用。一个内核线程会被多个用户级线程多路复用。每个内核级线程有一个可以轮流使用的用户线程集合。

    image-20220901233120160

    2.2.5 弹出式线程

    分布式系统中处理到来的消息:如服务请求。

    传统:将进程与线程阻塞在receive系统调用上,等待消息到达。消息到达时,系统调用接收消息。

    弹出式线程:一个消息的到达导致创建一个处理该消息的线程。好处是线程新,没有历史,可以快速创建。消息的到达和处理时间非常短。

    image-20220901235557180
  • 相关阅读:
    torchvision.datasets.ImageFolder使用详解
    基于nodejs的在线跑腿系统-计算机毕业设计
    使用Selenium和Java编写爬虫程序
    元宇宙侵权,虚拟世界还有法律吗?
    【读点论文】Densely Connected Convolutional Networks用残差连接大力出奇迹,进一步叠加特征图,以牺牲显存为代价
    C++征途 --- 模板 --- 类模板
    编程题总结 --- 2018
    【力扣每日一题】2023.9.24 LRU缓存
    Python网络爬虫实战:从入门到进阶
    【python刷题】--实战技巧总结(持续更新……)
  • 原文地址:https://blog.csdn.net/weixin_45745854/article/details/126657995