0%

操作系统-线程

1 简介

既然已经有了进程,为什么还要引入线程呢?或者说使用线程有何优点?

  • 线程拥有共享一个地址空间和所有可用数据的能力;
  • 线程比进程更轻量级,所以它们比进程更容易、更快创建及撤销;
  • 线程对于存在着大量的计算和大量的I/O处理的活动,能重叠执行,加快应用程序执行速度,提升性能。(注:多线程对于CPU密集型活动,并不能获得性能上的提升

进程拥有一个执行的线程,线程拥有:

  • 程序计数器:用于记录接着要执行的指令;
  • 寄存器:用于保存线程当前的工作变量(局部变量以及过程调用之后使用的返回地址);
  • 堆栈:用于记录执行历史,其中每一帧保存了一个一调用,但还未从中返回的过程;
  • 状态:与进程一直,有运行、阻塞、就绪、终止状态。状态间的转换和进程一致。

注:进程用于把资源集中到一起,而线程则是在CPU上被调度执行的实体。

2 多线程

在多线程的情况下,进程通常会从当前的单个线程开始。常见的线程调用如下:

  • 创建:这个线程有能力通过调用一个系统函数创建新线程(如:thread_create)。创建线程通常都会返回一个线程标识符,该标识符就是线程的名称。注:线程间通常是平等的,不存在父子关系(特殊情况除外)。
  • 退出:当一个线程执行完成后,可以通过调用一个库函数(如:thread_exit)退出。退出的线程不再被调度。
  • 等待其他线程:有时会需要等待其他线程退出,此时可通过调用一个库函数(如:thread_join)等待。此时当前线程将被阻塞,直到另一个线程退出。
  • 让出CPU:线程无法像进程一样通过时钟中断方式强制让出CPU,而某些场景下一个线程运行时间足够长,需要让另外一个线程执行,再进程操作。所以系统提供一个库函数(如:thread_yield)。

3 线程的实现

线程的实现主要有两种方式:在用户空间中实现和在内核中实现

为了实现可移植的线程程序,IEEE在IEEE标准1003.1c中定义了线程的标准。它定义的线程包叫做pthread。大部分UNIX系统都支持该标准。

3.1 用户空间中实现线程

在用户空间中实现线程,就是把整个线程包放在用户空间中,内核对线程包一无所知,即从内核的角度,就是按照正常的单线程进程管理方式。用户空间实现线程时,每个进程需要有其专用的线程表,用于跟踪进程中的线程。

优点:

  • 用户级线程可以在不支持线程的操作系统上实现;
  • 线程的切换不需要陷入内核,无需上下文切换,也无需对内存高速缓存进行刷新,使得线程的调度更加快捷;
  • 用户级线程允许每个进程有自己定制的调度算法,拥有较好的可扩展性;

不足:

  • 实现阻塞系统调用问题;
  • 缺页中断问题;
  • 从内核角度看,是按单线程进程管理的,即若一个线程开始运行,那么在该进程中的其他线程就不能运行,除非第一个线程自动放弃CPU。
3.2 内核中实现线程

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

所有线程的调用都以系统调用形式实现,相比用户空间实现,代价更大。有些系统采用“环保”的处理方式,即回收线程。具体过程为:当一个线程被撤销时,就把它标记为不可运行,但其内核数据结构并没有受到影响,在后续需要创建新线程时,重启某个旧线程,从而节省一些开支。

3.3 混合实现

使用内核级线程,然后将用户级线程与某些或者全部内核线程多路复用起来。

3.4 调度程序激活机制

调度程序是为了模拟内核线程的功能,为线程包提供在用户空间中才能实现的更好的性能和灵活性。解决内核级线程速度慢的问题。