使用应 用级并 发的应 用程序 称为并 发程序。 现代 操作系 统提供了三种 基本的 构造并 发程序 的方法 :
进程 。用这 种方法 ,每 个逻辑 控制流 都是一 个进程 ,由 内核来 调度和 维护。 因为进程有 独立的 虚拟地 址空间 ,想要 和其他 流通信 , 控制流 必须使 用某种 显式的 进程间communication, IPC) 机制。
I/0 多 路复用 。在这 种形式 的并发 编程中 ,应用 程序在 一个进 程的上 下文中 显式地调 度它们 自己的 逻辑流 。逻辑 流被模 型化为 状态机 ,数 据到达 文件描 述符后 ,主程序显 式地从 一个状 态转换 到另一 个状态 。因为 程序是 一 个单独 的进程 ,所以 所有的流都共 享同一 个地址 空间。
线程 。线程 是运行 在一个 单一进 程上下 文中的 逻辑流 ,由 内核进 行调度 。你 可以把线程看 成是其 他两种 方式的 混合体 ,像 进程流 一样由 内核进 行调度 ,而像 I/O 多路复用 流一样 共享同 一个虚 拟地址 空间。
进程方式:
通过接收到信息后创建多个进程,每个进程单独处理一个请求。
进程的优劣:
对 于在父 、子进 程间共 享状态 信息, 进程有 一个非 常清晰 的模型 :共享 文件表 ,但 是不共享用 户地址 空间。 进程有 独立的 地址空 间既是 优点也 是缺点 。这样 一来, 一个进 程不可 能不小心 覆盖另 一个进 程的虚 拟内存 ,这就 消除了 许多令 人迷惑 的错误 —— 这 是一个 明显的 优点。另 一方面 ,独立 的地址 空间使 得进程 共享状 态信息 变得更 加困难 。为 了共 享信息 ,它们必 须使用 显式的 IPC (进程 间通信 ) 机制 。(参 见下 面的旁 注。) 基于 进程的 设计的 另一个缺点是 ,它 们往往 比较慢 ,因 为进程 控制和 IPC 的开销 很高。
基于I/O多路复用的并发编程
基 本的思路就 是使用 select 函数 ,要求 内核挂 起进程 ,只有 在一个 或多个 I/O 事件 发生后 ,才将控制返 回给应 用程序。(个人理解想系统内IO处理方式)
事 件驱动 设计的 一个优 点是, 它比基 于进程 的设计 给了程 序员更 多的对 程序行 为的控制 。例如 ,我们 可以设 想编写 一个事 件驱动 的并发 服务器 ,为 某些客 户端提 供它们 需要的服务 ,而这 对于基 于进程 的并发 服务器 来说, 是很困 难的。另一个 优点是 ,一 个基于 I/O 多路复 用的事 件驱动 服务器 是运行 在单一 进程上 下文中的 ,因 此每个 逻辑流 都能访 问该进 程的全 部地址 空间。 这使得 在流之 间共享 数据变 得很容易 。一个 与作为 单个进 程运行 相关的 优点是 ,你 可以 利用熟 悉的调 试工具 ,例如 GDBÿ来调 试你的 并发服 务器, 就像对 顺序程 序那样 。最后 ,事 件驱 动设计 常常比 基于进 程的设计要高 效得多 , 因为它 们不需 要进程 上下文 切换来 调度新 的流。事 件驱动 设计一 个明显 的缺点 就是编 码复杂 。我 们的事 件驱动 的并发 echo 服务器 需要的代码 比基于 进程的 服务器 多三倍 ,并且 很不幸 ,随 着并 发粒度 的减小 ,复 杂性还 会上升 。这里的 粒度是 指每个 逻辑流 每个时 间片执 行的指 令数量 。
基于线 程的并 发编程
线程 (thread) 就是运 行在进 程上下 文中的 逻辑流 。在本 书里迄 今为止 ,程 序都 是由每个进程 中一个 线程组 成的。 但是现 代系统 也允许 我们编 写一个 进程里 同时运 行多个 线程的程序 。线 程由内 核自动 调度。 每个线 程都有 它自己 的线程 上下文 (thread context), 包括一个唯 一的整 数线程栈、 栈指针 、程序 计数器 、通用 目的寄 存器和条件码 。所有 的运行 在一个 进程里 的线程 共享该 进程的 整个虚 拟地址 空间。基于线 程的逻 辑流结 合了基 于进程 和基于 I/O 多路复 用的流 的特性 。同进 程一样 ,线程由 内核自 动调度 ,并 且内核 通过一 个整数 ID 来识别 线程。 同基于 I/O 多 路复用 的流一样 ,多 个线程 运行在 单一进 程的上 下文中 ,因此 共享这 个进程 虚拟地 址空间 的所有 内容,包括它 的代码 、数据 、堆 、共 享库和 打开的 文件。
Posix 线程 (Pthreads) 是在 C 程序中 处理线 程的一 个标准 接口。
线程通 过调用 pthread_create 函 数来创 建其他 线程。
回收已终止线程的资源。线程通 过调用 pthreadjoin 函数 等待其 他线程 终止。
分离线程 :在任何 一个时 间点上 ,线 程是可 结合的 (joinable) 或者是 分离的 (detached)。 一 个可结合 的线程 能够被 其他线 程收回 和杀死 。在被 其他线 程回收 之前, 它的内 存资源 (例 如栈) 是不 释放的 。相反 ,一 个分离 的线程 是不能 被其他 线程回 收或杀 死的。 它的内 存资源 在它终止 时由系 统自动 释放。
默认情况下 ,线 程被创建成可结合的 。为了避免内存泄漏 ,每个可结合线程都应该要么被其他线程 显式地收回, 要么通过调用 pthreacLdetach 函数被 分离。
初始 化线程:pthread_once 函 数允许 你初始 化与线 程例程 相关的 状态。
线程间同步使用信号量。典型的同步模型,消费者与生产者