并发编程:线程与进程
一、核心比喻
- 进程 = 工厂(独立资源,稳但重)
- 线程 = 工人(共享资源,快但易冲突)
- 一个进程可含多个线程
三种方案
1 | 方案 A:一个工厂,一个工人 |
二、进程是什么
进程 = 一个正在运行的程序。
- 独立:每个进程有自己的内存,互不干扰
- 安全:一个崩了,一般不影响别的
- 重量级:创建、切换开销大
- 通信麻烦:要走 IPC(管道、socket 等)
类比:每个工厂是独立的院子,围墙很高,想递东西得走大门。
三、线程是什么
线程 = 进程内部的一个执行流。
一个浏览器进程:
1 | 浏览器进程 |
- 共享:同一进程的线程共用内存
- 轻量:创建、切换开销小
- 通信快:直接读写共享内存
- 危险:一个线程崩了可能拖垮整个进程
类比:同一工厂的工人,共用厂房,交流方便,但一个闯祸全厂遭殃。
四、进程 vs 线程
| 对比项 | 进程 | 线程 |
|---|---|---|
| 是什么 | 运行中的程序 | 进程里的执行流 |
| 资源 | 独立 | 共享 |
| 内存 | 各自独立 | 共用 |
| 开销 | 大(重) | 小(轻) |
| 通信 | 麻烦(IPC) | 简单(共享变量) |
| 安全 | 一个崩了不影响别的 | 一个崩了可能全挂 |
| 数量级 | 几十 | 几百上千 |
| 比喻 | 工厂 | 工人 |
五、联系
包含关系:程序 > 进程 > 线程
- 没有进程,线程无处安身
- 一个进程至少有一个线程(主线程)
- 线程不能脱离进程单独存在
六、为什么用?
- 用进程:为了隔离和稳定(Chrome 每标签一进程)
- 用线程:为了并发和省资源(下载时还能点按钮)
做饭类比
| 场景 | 对应 |
|---|---|
| 一个厨房一个厨师 | 单进程单线程 |
| 一个厨房多个厨师 | 单进程多线程 |
| 多个厨房各做各的 | 多进程 |
七、并发问题:竞态条件
1 | count = 0 |
这叫”竞态条件”,需要加锁(Lock)解决。
类比:两个厨师同时往同一个碗里加盐,都以为自己是第一个。
取舍
- 进程:隔离好、通信难
- 线程:共享好、易冲突
八、Python 的 GIL
- 同一时刻,一个进程里只有一个线程能执行 Python 代码
- 多线程不能真正并行跑 CPU 密集任务
- 但适合 I/O 密集任务(等的时候可以切换)
选择
| 任务类型 | 选什么 | 例子 |
|---|---|---|
| I/O 密集 | 多线程 | 下载、读写文件、请求接口 |
| CPU 密集 | 多进程 | 计算、图像处理 |
口诀
“等”的任务用线程,”算”的任务用进程。
九、实战场景
- 爬虫:多线程/多进程并发抓取,快 N 倍
- Web 服务:每请求一线程/协程,同时服务很多人
- 数据清洗:多进程并行处理大文件
- 图像处理:多进程绕开 GIL
- 实时系统:注意线程竞争,加锁