http://www.kuqin.com/ace-2002-12/Part-One/Chapter-4.htm
4.3.2 基于线程的并发编程的好处
因为如下原因,在相互分离的线程、而不是进程中实现执行多任务的并发应用常常是有益的:
- 线程创建:不像生成新进程,派生一个新线程不需要(1)复制父地址空间内存,(2)设置新的内核数据结构,以及(3)消耗一个额外的进程槽,以在大的应用中执行子任务。
- 上下文切换:线程维护最小限度的状态信息,因而降低了上下文切换的开销,因为只须存储和取回少量状态信息。特别地,线程间的上下文切换消耗的时间比UNIX重量级进程间的上下文切换要少。这是由于在同一进程中的线程间切换时,无需改变TLB虚地址映射。而且,严格地在用户级运行的线程不会带来任何上下文切换开销。
- 同步:当调度和执行应用的线程时,可能不需要在内核模式和用户模式之间进行切换。进程同步比起线程同步要更为昂贵一些。例如,进行同步的实体常常不是全局的、而是局部的实体。全局同步总是涉及到内核,而应用线程所使用的局部(或“进程内”)同步则可能无需内核的干预。
- 数据拷贝:分离的线程间通过共享内存进行的通信常常比在分离的进程间使用IPC消息传递要快得多,因为前者避免了显式的数据拷贝的开销。例如,相互协作的数据库服务经常引用驻留内存的公用数据结构,通过线程来实现这些服务可能要更为简单和高效。一般而言,在线程间使用进程的共享地址空间进行通信通常比在进程间使用共享内存机制(比如系统V共享内存或内存映射文件)要更为容易和高效。
多进程(MP)和多线程(MT)
4.3.3 Solaris上的多进程和多线程综述
传统的UNIX进程是一种“重量级”的实体,其中含有一个单线程控制。相反,Solaris上可用的基于线程的并发机制要更为成熟、灵活和高效(在适当使用时)。如图4-2所示,Solaris MP/MT体系结构在两个层面上运作(内核空间和用户空间),并含有以下4种组件:
图4-2 Solairs 2.x多进程和多线程体系结构
- 处理单元(Processing element):这是执行用户级和内核级指令的CPU。Sun MP/MT模型的语义意图能同时用于单处理器和共享内存的硬件上的对称多处理器。
- 内核线程(Kernel thread):这是处理单元(PE)在内核空间中调度和执行的基本实体。OS内核为每一内核线程维护一个小数据结构和栈。内核线程间的上下文切换相对较快,因为它不要求改变虚拟内存信息。
- 轻量级进程(Lightweight process,或LWP):它们与内核线程相关联。在Solaris 2.x中,一个UNIX进程不再是一个线程控制,而是在其中含有一或多个LWP。在LWP和它的内核线程间存在着1对1的映射。Solaris中的内核级调度器使用LWP(因而也包括内核进程)来调度应用任务。LWP含有数量相对较多的状态(比如寄存器数据、账务和特征信息、虚拟内存地址范围和定时器)。因而,LWP间的上下文切换相对较慢。
对于分时调度器类别(缺省),调度器通过“占先”(preemption)将可用的PE在多个活动的LWP间进行划分。通过这种技术,每个LWP运行一段有限的时间(通常为10毫秒)。当前LWP的时间片到期后,OS调度器选择另一个可用的LWP,执行一次上下文切换,并将被占先的LWP放置到一个队列中。内核使用若干标准(比如优先级、资源可用性、调度类别,等等)来调度LWP。在分时调度器类别中,没有固定的LWP执行顺序。
- 应用线程:每个LWP可被认为是一个“虚拟PE”,在其上应用线程被一个用户级的线程库调度和多路复用。每个应用线程与其他线程一起共享它的进程地址空间,尽管它拥有唯一的栈和寄存器组。应用线程还可以派生出其他应用线程。在进程中,每个这样的应用线程都独立地执行(尽管取决于硬件、并非必然地并发执行)
- 绑定线程(Bound thread):它被1对1地映射到LWP和内核线程。绑定线程允许独立任务在多PE上并行执行。因而,如果两个应用线程运行在分离的LWP上(因而也是分离的内核线程上),它们也就可以并行地执行(假定它们运行在多处理器上,或使用异步I/O)。而且,应用线程可以执行阻塞的系统调用并处理页错误,而不会阻止其他应用线程的运行。
重新调度绑定线程需要一次内核级上下文切换。同样地,绑定线程上的同步操作也需要OS内核的干预。当应用被设计利用在硬件平台上可用的并行性优点时,绑定线程最为有用。因为每个绑定线程都要求分配内核资源,分配大量绑定线程可能导致效率低下。
- 非绑定线程:它们被一个线程运行时库以多对多的方式在一或多个LWP和内核线程上多路复用。该用户级库实现了一个非占先的协作式多任务并发模型。在使内核干预最少化的同时,它调度、分派,并挂起非绑定线程。与使用绑定到LWP的应用线程相比较,非绑定应用线程的派生、上下文切换和同步所需的开销较小。
取决于应用和/或库与一个进程相关联的内核线程的数目,可以在多PE上并行执行一或多个非绑定线程。因为每个非绑定线程并不分配内核资源,有可能分配数量相当大的非绑定线程,而不会显著地降低性能。
一个相关的问题是在某个线程中调用的某些UNIX系统调用可能对整个进程产生不希望产生的副作用。例如,exit系统调用具有销毁进程中所有线程的副作用(应使用thr_exit来终止当前线程)。
细粒度的锁定会导致高昂的同步开销。
多线程的另一局限是一个进程中的所有线程共享同样的用户id和访问文件和其他受保护资源的特权。
四种主要同步机制的C++包装:互斥体、读者/作者锁、计数信号量,以及条件变量