第 1 节 背景
为了更好的理解多线程的概念, 先对进程, 线程的概念背景做一下简单介绍。 早期的计
算机系统都只允许一个程序独占系统资源, 一次只能执行一个程序。 在大型机年代, 计算能
力是一种宝贵资源。 对于资源拥有方来说, 最好的生财之道自然是将同一资源同时租售给尽
可能多的用户。最理想的情况是垄断全球计算市场。所以不难理
解为何当年 IBM 预测 “全球只要有 4 台计算机就够了 ”。
这种背景下, 一个计算机能够支持多个程序并发执行的需求变得十分迫切。 由此产生了
进程的概念。进程在多数早期多任务操作
系统中是执行工作的基本单元。 进程是包含程序指令和相关资源的集合。 每个进程和其他进
程一起参与调度,竞争 CPU,内存等系统资源。每次进程切换,都存在进程资源的保存和
恢复动作,这称为上下文切换。
进程的引入可以解决支持多用户的问题, 但是多进程系统也在如下方面产生了新的问题:
进程频繁切换引起的额外开销可能会严重影响系统性能。
进程间通信要求复杂的系统级实现。
在程序功能日趋复杂的情况下, 上述缺陷也就凸现出来。 比如,一个简单的 GUI 程序,
为了有更好的交互性, 通常用一个任务支持界面交互, 另一个任务支持后台运算。 如果每个
任务均由一个进程来实现, 那会相当低效。 对每个进程来说, 系统资源看上去都是其独占的。
比如内存空间, 每个进程认为自己的内存空间是独有的。 一次切换, 这些独立资源都需要切
换。
由此就演化出了利用分配给同一个进程的资源,尽量实现多个任务的方法。这也就引入
了线程的概念。 同一个进程内部的多个线程,共享的是同一个进程的所有资源。 比如,与每
个进程独有自己的内存空间不同, 同属一个进程的多个线程共享该进程的内存空间。 例如在
进程地址空间中有一个全局变量 globalVar ,若 A 线程将其赋值为 1,则另一线程 B 可以看
到该变量值为 1。两个线程看到的全局变量 globalVar 是同一个变量。通过线程可以支持同
一个应用程序内部的并发,免去了进程频繁切换的开销,另外并发任务间通信也更简单。
目前多线程应用主要用于两大领域: 网络应用和嵌入式应用。 为什么在这两个领域应用
较多呢?因为多线程应用能够解决两大问题: 并发。 网络程序具有天生的并发性。 比如网络
数据库可能需要同时处理数以千计的请求。而由于网络连接的时延不确定性和不可靠性, 一
旦等待一次网络交互, 可以让当前线程进入睡眠,退出调度, 处理其他线程。这样就能够有
效利用系统资源, 充分发挥系统 实时处理能力。 线程的切换是轻量级的, 所以可以保证足
够快。 每当有事件发生,状态改变, 都能有线程及时响应,而且每次线程内部处理的计算强
度和复杂度都不大。 在这种情况下,多线程实现的模型也是高效的。 在有些语言中,对多线
程或者并发的支持是直接内建在语言中的,比如 Ada 和 VHDL 。在 C++里面,对多线程的
支持由具体操作系统提供的函数接口支持。 不同的系统中具体实现方法不同。 后面所有例子
只给出 windows 和 Unix/Linux 的实现。在后面的实现中,考虑的是尽量封装隔离底层的多
线程函数接口, 屏蔽操作系统底层的线程实现具体细节, 介绍的重点是多线程编程中较通用
的概念。 同时也尽量体现 C++面向对象的一面。 最后,由于空闲时间有限, 我只求示例代码
能够明确表达自己的意思即可。至于代码的尽善尽美就只能有劳各位尽力以为之了。
第 2 节 线程的创建
本节介绍如下内容:线程状态、线程运行环境、线程类定义
示例程序:线程类的 Windows 和 Unix 实现
线程状态