进程与线程
→ 返回计算机基础
进程是操作系统资源分配的基本单位,线程是 CPU 调度的基本单位。理解两者的区别是掌握并发编程的前提。
进程
进程是一个正在运行的程序实例,拥有独立的地址空间、文件描述符、信号处理等资源。
进程状态
创建
↓
就绪(Ready)←──── 运行(Running)
↑ │
│ 时间片到/抢占 │ 等待 IO 或资源
└────────────── 阻塞(Blocked)
↑
IO 完成/资源就绪
运行 ──exit──→ 终止(Terminated)
PCB(进程控制块)
OS 用 PCB 描述和管理进程,包含:进程标识(PID)、CPU 寄存器状态、内存管理信息(页表基址)、IO 状态(打开的文件描述符)、调度信息(优先级、时间片)。
线程
线程是进程内的执行流,共享进程的地址空间和资源,但拥有独立的程序计数器(PC)、寄存器组和栈。
进程 vs 线程
| 进程 | 线程 | |
|---|---|---|
| 资源 | 独立地址空间 | 共享进程资源 |
| 创建开销 | 大(fork 复制地址空间) | 小 |
| 通信 | IPC(管道、共享内存等) | 直接读写共享变量 |
| 崩溃影响 | 不影响其他进程 | 会导致整个进程崩溃 |
| 切换开销 | 大(需切换地址空间) | 小 |
线程模型
| 模型 | 说明 | 示例 |
|---|---|---|
| 用户态线程 | 由用户库管理,内核不感知,切换快 | 协程、早期 goroutine |
| 内核态线程 | 由 OS 调度,可利用多核 | Linux pthread |
| 混合模型(M:N) | M 个用户线程映射到 N 个内核线程 | Go 运行时、Java 虚拟线程 |
上下文切换
CPU 从执行 A 切换到 B 的过程:
1. 保存 A 的寄存器、PC 等到 PCB/TCB
2. 调度器选择下一个运行的进程/线程
3. 从 B 的 PCB/TCB 恢复寄存器状态
4. 跳转到 B 的 PC 继续执行
进程切换还需刷新 TLB(切换地址空间),开销比线程切换更大。
进程间通信(IPC)
| 方式 | 特点 | 适用场景 |
|---|---|---|
| 管道(Pipe) | 单向,父子进程间,内核缓冲 | Shell 管道 | |
| 命名管道(FIFO) | 双向,无关进程间 | 本地进程通信 |
| 信号(Signal) | 异步通知,无数据 | 进程控制(SIGKILL、SIGTERM) |
| 共享内存 | 最快,需同步机制 | 高性能本地通信 |
| 消息队列 | 有格式的消息传递 | 解耦生产/消费 |
| 信号量(Semaphore) | 计数器,用于同步和互斥 | 资源访问控制 |
| Socket | 跨网络通信 | 分布式系统 |
线程同步
多线程共享数据时需防止竞态条件(Race Condition):
| 机制 | 说明 |
|---|---|
| 互斥锁(Mutex) | 同一时刻只有一个线程进入临界区 |
| 读写锁(RWLock) | 允许多读者并发,写者独占 |
| 信号量(Semaphore) | 控制对 N 个资源的并发访问 |
| 条件变量(Cond) | 配合互斥锁,条件不满足时阻塞,满足时唤醒 |
| 原子操作(Atomic) | 硬件级别的不可分割操作,无需加锁 |
协程(Coroutine)
协程是用户态的轻量级”线程”,由程序自主调度(非抢占),切换开销极小:
| 线程 | 协程 | |
|---|---|---|
| 调度者 | 操作系统 | 运行时/用户程序 |
| 切换开销 | 较大(内核态) | 极小(用户态) |
| 并行 | 可真正并行 | 单线程内并发 |
| 适用 | CPU 密集 | IO 密集 |