第 4 章 线程
概述
每个线程是 CPU 使用的一个基本单元,与同一线程的其它线程共享代码段、数据段和其他操作系统资源。
- 多线程编程具有如下四大类的优点:
- 响应性:一个采用多线程的交互程序即使部分阻塞或者执行冗长操作,仍可以继续执行,从而增加对用户的相应程度
- 资源共享:线程默认共享它们所属进程的内存和资源
- 经济:进程创建所需的内存和资源更昂贵,创建和切换线程更加经济
- 可伸缩性:线程可在多处理器核上并行运行
多核编程
并发性(concurrency):并发系统允许支持多个任务,允许所有任务都能取得进展(单核通过切换任务也能完成)
并行性(parallelism):并行系统可以同时执行多个任务
并发不一定并行,并行一定并发
多线程模型
用户线程:通过用户层面的线程库实现线程管理,位于内核之上,无需内核支持,缺点是内核享受不到多线程带来的好处
内核线程:由操作系统来直接支持和管理
多对一模型
映射多个用户级线程到一个内核线程
一对一模型
映射每个用户线程到一个内核线程、
优点是在一个线程执行阻塞系统调用时,能够允许另一个线程继续执行,所以提供了比多对一模型更好的并发功能;也允许多个线程并行运行在多处理器系统上。
缺点是创建一个用户线程就要创建一个相应的内核线程,而创建内核线程的开销会影响应用程序的性能,所以这种模型的大多数实现限制系统支持的线程数量。
多对多模型
多路复用多个用户级线程到同样数量或更少数量的内核线程
线程库
线程库为程序员提供创建和管理线程的 API
- 实现线程库的主要方法有两种:
- 在用户空间中提供一个没有内核支持的库,库的所有代码和数据结构都位于用户空间
- 实现由操作系统直接支持的内核级的一个库,调用一个 API 函数通常导致对内核的系统调用
POSIX Pthreads、Windows 线程、Java 线程
多线程问题
系统调用fork()
和exec()
- 线程调用
fork()
,有两种形式:- 新进程复制所有线程
- 新进程只有单个线程,仅仅复制调用了
fork()
的线程
如果一个线程调用exec()
系统调用,exec()
参数指定的程序将会取代整个进程,包括所有线程。
如果分叉后立即调用exec()
,则没有必要复制所有线程,只需复制调用线程。
信号处理
UNIX 信号用于通知进程某个特定事件已经发生
信号的接收可以是同步或异步的,取决于事件信号的来源和原因。
- 所有信号遵循相同的模式:
- 信号是由特定事件的发生而产生的
- 信号被传递给某个进程
- 信号一旦收到就应处理
- 信号处理程序可以分为两种:
- 缺省的信号处理程序
- 用户定义的信号处理程序
- 信号传递通常具有如下选择:
- 传递信号到信号所适用的线程
- 传递信号到进程内的每个线程
- 传递信号到进程内的某些线程
- 规定一个特定线程以接收进程的所有信号
线程撤销
线程撤销(Thread Cancellation)是在线程完成之前终止进程
- 目标线程的撤销可以有两种情况:
- 异步撤销:一个线程立即终止目标线程
- 延迟撤销:目标线程不断检查它是否应终止,这允许目标线程有机会有序终止自己
线程池
在进程开始时创建一定数量的线程,并加到池中以等待工作
- 使用线程池的优点:
- 用现有线程服务请求比等待创建一个线程更快
- 线程池限制了任何时候可用线程的数量
线程本地存储
除了共享进程的数据,每个线程可以拥有自己的数据,称为线程本地存储(Thread-Local Storage, TLS)
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 OE.Heart's Blog!