迄今为止,我们只是泛泛地讨论了线程。不过,有两种不同方法来提供线程支持:用户层的用户线程或内核层的内核线程。
用户线程位于内核之上,它的管理无需内核支持;而内核线程由操作系统来直接支持与管理。几乎所有的现代操作系统,包括 Windows、Linux、Mac OS X 和 Solaris,都支持内核线程。
最终,用户线程和内核线程之间必然存在某种关系。本节研究三种常用的建立这种关系的方法:多对一模型、一对一模型和多对多模型。
多对一模型(图 1)映射多个用户级线程到一个内核线程。
线程管理是由用户空间的线程库来完成的,因此效率更高。不过,如果一个线程执行阻塞系统调用,那么整个进程将会阻塞。再者,因为任一时间只有一个线程可以访问内核,所以多个线程不能并行运行在多处理核系统上。
Green threads 线程库为 Solaris 所采用,也为早期版本的 Java 所采纳,它就使用了多对一模型。然而,现在几乎没有系统继续使用这个模型,因为它无法利用多个处理核。
一对一模型(图 2)映射每个用户线程到一个内核线程。
该模型在一个线程执行阻塞系统调用时,能够允许另一个线程继续执行,所以它提供了比多对一模型更好的并发功能;它也允许多个线程并行运行在多处理器系统上。
这种模型的唯一缺点是,创建一个用户线程就要创建一个相应的内核线程。由于创建内核线程的开销会影响应用程序的性能,所以这种模型的大多数实现限制了系统支持的线程数量。Linux,还有 Windows 操作系统的家族,都实现了一对一模型。
多对多模型(图 3)多路复用多个用户级线程到同样数量或更少数量的内核线程。内核线程的数量可能与特定应用程序或特定机器有关(应用程序在多处理器上比在单处理器上可能分配到更多数量的线程)。
现在我们考虑一下这些设计对并发性的影响。虽然多对一模型允许开发人员创建任意多的用户线程,但是由于内核只能一次调度一个线程,所以并未增加并发性。虽然一对一模型提供了更大的并发性,但是开发人员应小心,不要在应用程序内创建太多线程(有时系统可能会限制创建线程的数量)。
多对多模型没有这两个缺点:开发人员可以创建任意多的用户线程,并且相应内核线程能在多处理器系统上并发执行。而且,当一个线程执行阻塞系统调用时,内核可以调度另一个线程来执行。
多对多模型的一种变种仍然多路复用多个用户级线程到同样数量或更少数量的内核线程,但也允许绑定某个用户线程到一个内核线程。这个变种,有时称为双层模型(图 4)。
Solaris 操作系统在第 9 版以前支持这种双层模型;但从第 9 版后,就使用了一对一模型。