本文摘自《多线程编程指南》
多线程一词可以解释为多个控制线程或多个控制流。
在代码中实现多线程具有以下益处:
■
提高应用程序的响应
■
更有效地使用多处理器
■
改进程序结构
■
占用较少的系统资源
多线程的基本概念
并发性和并行性
在单个处理器的多线程进程中,处理器可以在线程之间切换执行资源,从而执行并发。
多线程结构一览
传统的
UNIX 已支持多线程的概念。每个进程都包含一个线程,因此对多个进程进行编程即是对多个线程进行编程。但是,进程同时也是一个地址空间,因此创建进程会涉及到创建新的地址空间。创建线程比创建新进程成本低,因为新创建的线程使用的是当前进程的地址空间。相对于在进程之间切换,在线程之间进行切换所需的时间更少,因为后者不包括地址空间之间的切换。在进程内部的线程间通信很简单,因为这些线程会共享所有内容,特别是地址空间。所以,一个线程生成的数据可以立即用于其他所有线程。
线程是多线程编程中的主编程接口。线程仅在进程内部是可见的,进程内部的线程会共享诸如地址空间、打开的文件等所有进程资源。
以下状态对于每个线程是唯一的。
■
线程 ID
■
寄存器状态(包括 PC
和栈指针)
■
栈
■
信号掩码
■
优先级
■
线程专用存储
线程调度
POSIX 标准指定了三种调度策略:先入先出策略 (SCHED_FIFO)、循环策略 (SCHED_RR)和自定义策略 (SCHED_OTHER)。
SCHED_FIFO 是基于队列的调度程序,对于每个优先级都会使用不同的队列。
SCHED_RR 与 FIFO 相似,不同的是前者的每个线程都有一个执行时间配额。
SCHED_FIFO 和 SCHED_RR 是对 POSIX Realtime 的扩展。SCHED_OTHER 是缺省的调度策略。
提供了两个调度范围:进程范围
(PTHREAD_SCOPE_PROCESS)
和系统范围(PTHREAD_SCOPE_SYSTEM)。具有不同范围状态的线程可以在同一个系统甚至同一个进程中共存。进程范围只允许这种线程与同一进程中的其他线程争用资源,而系统范围则允许此类线程与系统内的其他所有线程争用资源。实际上,从
Solaris 9
发行版开始,系统就不再区分这两个范围。
线程取消
一个线程可以请求终止同一个进程中的其他任何线程。目标线程(要取消的线程)可以延后取消请求,并在该线程处理取消请求时执行特定于应用程序的清理操作。通过 pthread 取消功能,可以对线程进行异步终止或延迟终止。异步取消可以随时发生,而延迟取消只能发生在所定义的点。延迟取消是缺省类型。
使用同步功能,可以控制程序流并访问共享数据,从而并发执行多个线程。
共有四种同步模型:互斥锁、读写锁、条件变量和信号。
■ 互斥锁仅允许每次使用一个线程来执行特定的部分代码或者访问特定数据。
■ 读写锁允许对受保护的共享资源进行并发读取和独占写入。要修改资源,线程必须首先获取互斥写锁。只有释放所有的读锁之后,才允许使用互斥写锁。
■ 条件变量会一直阻塞线程,直到特定的条件为真。
■ 计数信号量通常用来协调对资源的访问。使用计数,可以限制访问某个信号的线程数量。达到指定的计数时,信号将阻塞
使用 64位体系结构
对于应用程序开发者,
Solaris 64
位和 32
位环境的主要区别在于所使用的 C
语言数据类型的模型。
64 位数据类型使用
LP64 模型,其中
long 和指针的宽度为
64 位,其他所有基础数据类型仍然与
32
位实现的数据类型相同。 32
位数据类型使用 ILP32
模型,其中的 int、
long
和指针宽度为
32 位。以下简要概述了
64
位环境的主要特征以及使用该环境时的注意事项:
■
大虚拟地址空间
在
64 位环境中,进程的虚拟地址空间最高可达
64 位(即
18 EB)。目前,
32 位进程的最大地址空间为
4 GB,较大的虚拟地址空间大约是其
40
亿倍。但是由于硬件限制,某些平台可能并不支持完整的
64 位地址空间。大地址空间增加了可创建的具有缺省栈大小的线程数。在
32
位和 64
位系统中,栈的大小分别为
1 MB 和
2 MB。 在
32 位和
64 位系统中,具有缺省栈大小的线程数分别是大约2000
个和
80000 亿个。
■
内核内存读取器
内核是在内部使用
64 位数据结构的
LP64 对象。这意味着,使用
libkvm、
/dev/mem 或/dev/kmem
的现有
32 位应用程序不能正常工作,必须转换为
64 位程序。
■
/proc 限制
使用
/proc 的
32 位程序可以查看
32 位进程,但是无法识别
64 位进程。用来描述进程的现有接口和数据结构不够大,因此无法包含
64
位值。对于此类程序,必须将其重新编译为
64 位程序,使其可同时适用于
32 位进程和
64 位进程。
■
64 位库
32
位库必须与 32
位应用程序进行链接,而 64
位库必须与 64
位应用程序进行链接。除已过时的库以外,所有的系统库都同时提供
32 位版本和
64 位版本。
■
64 位运算
64
位运算早已在以前的 32
位 Solaris
发行版中提供。现在, 64
位实现提供了完整的 64
位计算机寄存器,用于进行整数运算和参数传递。
■
大文件
如果应用程序仅要求大文件支持,则可以保留
32 位并使用大文件接口。要充分利用
64位功能,必须将应用程序转换为
64 位。