在传统操作系统中,每个进程有一个地址空间和一个控制线程。不过经常存在一个地址空间中准并行运行多个控制线程的情形,这些线程就像(差不多)分离的进程(共享地址空间除外)。
使用多线程的原因:
多线程模型中:并行实体有共享同一个地址空间和所有可用数据的能力。
Example:文件编辑器的例子——三个线程能够代替三个进程
进程模型基于两种独立的概念:资源分组处理与执行。将这两种概念分开,就引入了“线程”的概念。
理解进程的一个角度是:用某种方法将相关的资源集中在一起。
进程有存放程序正文和数据以及其他资源的地址空间,这些资源中包含打开的文件、子进程、即将发生的定时器、信号处理程序、账号信息等。把他们都放到进程中更容易管理。
进程拥有一个执行的线程,通常简写为线程。线程中有:
进程用于将资源集中在一起,线程是在CPU上被调度的实体。
同一个进程中并行运行多个线程,是对在同一台计算机中并行运行多个进程的模拟。
在2-11a中,可以看到三个传统的进程。每个进程都有自己的地址空间和单个控制线程。每个线程在不同的地址空间中运行。
在2-11b中,可以看到一个进程带有三个控制线程。每个线程在相同的地址空间中运行。
进程中的线程,所有线程有相同的地址空间,共享同样的全局变量。一个线程可以读、写另一个线程的堆栈,甚至可以清除另一个线程的堆栈。线程之间是没有保护的。
线程的状态和进程一样:
每个线程的堆栈有一帧,供各个被调用但是还没有从中返回的过程使用。在该栈帧中存放了相应过程的局部变量以及过程调用完成之后使用的返回地址。
通常每个线程会调用不同的过程,从而有一个各自不同的执行历史,故每个线程都要有自己的堆栈。
多线程的情况下,进程通常会从当前的单个线程开始。线程调用库函数(如thread_create
)创建。thread_create()
参数指定了新线程要运行的过程名。
线程之间是平等的,不论有无层次关系,创建线程通常都会返回一个线程标识符,该标识符是新线程的名字。
thread_exit
线程完成工作后,可以通过一个库过程(如thread_exit
)退出,接着该线程消失,不再可调度。这种情况下,现成的创建和终止类似于进程的创建和终止。
thread_join
某些线程调用中,例如thread_join
,可以线程可以等待一个(特定)线程退出。这个过程堵塞调用直到那个特殊的线程退出。
thread_yeild
允许线程自动放弃CPU从而让另一个线程运行。
因为线程不同于进程,所以无法利用时钟中断强制线程让出CPU。
IEEE定义了线程的标准。定义的线程包叫做pthread
。大部分UNIX支持该标准。
所有pthread
线程都有某些特性。每一个都有一个标识符,一组寄存器和一组存储在结构中的属性。这些属性包括堆栈大小、调度参数以及其他线程需要的项目。
pthread_attr_init
建立一个线程的属性结构,并初始化成默认值。
pthread_attr_destory
删除一个线程的属性数据结构,释放它占用的内存。不会影响调用它的线程,这些线程会继续存在。
有两种主要的方式实现线程包:在用户空间和在内核中。这两种方法互有利弊,不过混合实现的方式也有可能。
把整个线程包放在用户空间中,内核对线程包一无所知。从内核的角度考虑,就是按照正常的方式进行管理:单线程进程。线程在一个运行时系统的上层运行,该运行时系统是一个管理线程的过程的集合。如:pthread_create
,pthread_exit
,pthread_join
和pthread_yeild
等。
在用户空间管理线程时,每个进程需要有其专用的线程表,用来跟踪该进程中的线程。线程表与进程表相似,不过仅仅记录各个线程的属性,如每个线程的程序计数器、堆栈指针、寄存器和状态等。线程表由运行时系统管理。
在用户空间实现的优点:
thread_yeild
后调用线程调度程序来选择另一个要运行的程序,调度是本地过程。在用户空间实现的缺点:
在内核中实现线程,不再需要运行时系统,进程中也没有线程表。
内核中有用来记录系统中所有线程的线程表。当某个线程希望创建一个新线程或者撤销一个已有的线程时,他进行一个系统调用,这个系统调用通过对线程表的更新完成线程的创建或者撤销。
线程表维护的信息:
与在用户空间中一样。另外,内核维护了传统的进程表,以跟踪进程的状态。
优点:
缺点:
但是内核中创建或者撤销线程的代价比较大
引入内核级线程,然后将用户线程与全部内核线程多路复用起来。
编程人员可以决定多少个内核级线程和多少个用户级线程彼此多路复用。带来了最大的灵活度。
内核只识别内核级线程,并对其进行调用。一个内核线程会被多个用户级线程多路复用。每个内核级线程有一个可以轮流使用的用户线程集合。
分布式系统中处理到来的消息:如服务请求。
传统:将进程与线程阻塞在receive
系统调用上,等待消息到达。消息到达时,系统调用接收消息。
弹出式线程:一个消息的到达导致创建一个处理该消息的线程。好处是线程新,没有历史,可以快速创建。消息的到达和处理时间非常短。