核心概念速览

并发 vs 并行

通俗例子:

  • 不支持并发/并行: 吃完饭才接电话
  • 并发: 停下吃饭去接电话,接完继续吃 (快速切换)
  • 并行: 一边吃饭一边打电话 (同时进行)

本质区别:

  • 并发: 有处理多个任务的能力,但不一定同时 (单核 CPU 时间片轮转)
  • 并行: 能同时处理多个任务 (多核 CPU 真正并行)

同步 vs 异步

  • 同步: 等待操作完成才继续 (如:等快递送到才出门)
  • 异步: 不等待,继续执行,完成后通知 (如:快递到了发短信通知)

阻塞 vs 非阻塞

  • 阻塞: 调用时被操作系统挂起,必须等待
  • 非阻塞: 调用后立即返回,不会被挂起

进程 (Process)

定义: 资源分配的最小单位,程序运行的实例。

特点:

  • 独立的内存空间、文件描述符、系统资源
  • 进程间相互独立,互不干扰
  • 切换开销大 (需要保存/恢复完整上下文)

举例: 聊 QQ 和看视频是两个独立的进程

线程 (Thread)

定义: 程序执行的最小单位,也称”微进程”。

特点:

  • 共享所属进程的资源 (内存、文件等)
  • 切换开销较小 (共享资源,只需切换少量上下文)
  • 存在资源竞争问题 (如 Python 的 GIL 锁)

举例: 播放器中,视频画面、声音输出、进度条拖动是不同的线程,它们协同工作让体验流畅。

协程 (Coroutine)

定义: 用户态的轻量级线程,程序自己控制切换。

优点:

  • ✅ 极低的切换开销 (用户态切换,无需系统调用)
  • ✅ 高并发支持 (单核可支持上万协程)
  • ✅ 无需锁机制 (单线程内切换)

缺点:

  • ❌ 无法利用多核 (本质是单线程)
  • ❌ 阻塞操作会阻塞整个程序

适用场景: I/O 密集型任务 (网络请求、文件读写)


三者对比

对比维度 进程 线程 协程
定位 资源分配单位 执行调度单位 用户态轻量级线程
调度者 操作系统 操作系统 程序自身
资源隔离 ✅ 独立内存空间 ❌ 共享进程资源 ❌ 共享线程资源
并行能力 ✅ 真并行 (多核) ⚠️ 受 GIL 限制 ❌ 单线程内切换
切换代价 高 (内核态) 中 (系统调用) 极低 (用户态)
适用场景 CPU 密集型 I/O 密集型 高并发 I/O

形象比喻:工厂模型

  • 程序 = 设计图纸
  • 进程 = 独立工厂 (有自己的土地、机器、电力)
  • 线程 = 工厂里的工人 (共享工厂资源)
  • GIL (Python) = 工厂机器室的唯一钥匙 (同一时刻只有一个工人能操作)
  • 协程 = 聪明的工人 (等待烤箱时主动去做其他事,烤好了再回来)

最佳实践

为更直观地理解 Python 中的进程、线程与协程,使用“工厂”比喻:

  1. 设计图 → 程序(Program)
    程序就像工厂的设计蓝图,尚未运行时只是计划。

  2. 工厂 → 进程(Process)
    蓝图投入运行后,操作系统会分配土地(内存)、机器(CPU 时间片)、接通电源(系统资源),蓝图变成一座真实的工厂。每座工厂独立,互不干扰。
    • 进程 = 被系统赋予资源后、真正“开工”的程序实例。

  3. 工人 → 线程(Thread)
    工厂需要工人来操作机器,工人即线程。工人共享工厂资源,各自负责不同环节(主工人负责调度、记录,其他工人负责运输、组装等)。
    • 线程 = 在同一工厂中共享资源的执行单元。

  4. 钥匙(GIL)→ Python 的执行控制机制
    Python 工厂的机器室只有一把钥匙(GIL)。每个工人操作机器前必须拿到钥匙,任意时刻只有一个工人能工作,完成后交回钥匙。这样防止资源竞争。
    • GIL = 保证同一时间只有一个工人能操作机器(互斥执行)。

  5. 厂区调度系统 → 操作系统调度器
    工业区(操作系统)同时管理多座工厂(进程),决定谁先用电、谁暂停、何时切换(如 CFS)。
    • 调度进程的是操作系统;线程的调度受操作系统 + GIL 影响。

  6. 工人自我调度 → 协程(Coroutine)
    有些工人会自我管理,主动切换任务以提高效率(例如:烘焙时把面团放进烤箱后去揉下一份,烤箱响再回来取)。
    • 协程 = 工人自己决定何时切换任务,切换轻量且非抢占式。

  7. 对比小表

概念 类比 调度者 是否并行 是否共享资源 切换代价
进程 工厂 操作系统 ✅ 真并行(多核) ❌ 否 高(内核级)
线程 工人 操作系统 + GIL ⚠️ 伪并行(同一时刻一人) ✅ 是 中(系统级)
协程 自我调度的工人 工人自身 ❌ 并发(非并行) ✅ 是 极低(用户态)
  1. 完整故事(简述)
    一份蓝图(程序)被批准投产后成为工厂(进程)。工厂雇来工人(线程),但机器室只有一把钥匙(GIL),工人们轮流使用机器。工业区调度中心(操作系统)决定哪座工厂先用电。聪明的工人(协程)会在等待 I/O 时切换任务,大幅提高效率。

  2. 一句话总结
    进程 = 工厂(资源容器);线程 = 工人(执行单元);GIL = 唯一钥匙(互斥);操作系统 = 厂区调度;协程 = 自主切换的聪明工人。

(可按需将此节放在 最佳实践 之后或作为附录)

最佳实践

场景选择

任务类型 推荐方案 原因
CPU 密集型 多进程 绕开 GIL,充分利用多核
I/O 密集型 多线程/协程 等待 I/O 时可切换到其他任务
高并发 I/O 协程 极低的切换开销,支持海量并发
混合场景 多进程 + 协程 进程利用多核,协程提升单核效率

代码示例

CPU 密集型:使用多进程

1
2
3
4
5
6
7
from multiprocessing import Pool

def cpu_bound_task(n):
return sum(i * i for i in range(n))

with Pool(4) as p:
results = p.map(cpu_bound_task, [10**7] * 4)

I/O 密集型:使用协程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import asyncio
import aiohttp

async def fetch_url(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.text()

async def main():
urls = ['http://example.com'] * 100
tasks = [fetch_url(url) for url in urls]
return await asyncio.gather(*tasks)

asyncio.run(main())

关键原则

  1. CPU 密集 → 多进程 (计算、循环、数据处理)
  2. I/O 密集 → 协程 > 线程 (网络、文件、数据库)
  3. 高性能 → 多进程 + 协程 (充分利用硬件 + 高效调度)