Python中多线程、多进程、协程的对比

1. 基本概念

在Python中,多线程、多进程和协程是实现并发编程的三种主要方式,它们各有特点和适用场景。

1.1 多线程 (Threading)

特点

  • 使用threading模块
  • 在单个进程内创建多个执行线程
  • 线程共享同一进程的内存空间

适用场景

  • I/O密集型任务(网络请求、文件读写)
  • 需要共享状态的并发任务
  • GUI应用中的后台任务

注意事项

  • 注意线程安全问题(使用Lock等同步机制)
  • 避免长时间占用GIL的CPU操作

1.2 多进程 (Multiprocessing)

特点

  • 使用multiprocessing模块
  • 创建多个独立的进程
  • 每个进程有自己独立的内存空间

适用场景

  • CPU密集型任务(数学计算、图像处理)
  • 需要真正并行执行的任务
  • 需要隔离内存空间的任务

注意事项

  • 进程创建和销毁开销大
  • 进程间通信(IPC)成本高

1.3 协程 (Coroutine)

特点

  • 使用asyncio模块或第三方库如gevent
  • 单线程内的协作式多任务
  • 通过事件循环和await/async语法实现

适用场景

  • 高并发的I/O密集型任务(如Web服务器)
  • 需要轻量级并发的场景
  • 需要处理大量网络连接

注意事项

  • 需要所有代码都支持async/await
  • 阻塞操作会阻塞整个事件循环

2. 核心区别

对比分析表

特性 多线程 多进程 协程
执行方式 抢占式调度 并行执行 协作式调度
内存使用 共享内存 独立内存 共享内存
创建开销 较小 较大 最小
数据共享 容易(但有风险) 需要IPC机制 容易
GIL影响 受GIL限制 不受GIL限制 不受GIL限制
适用场景 I/O密集型 CPU密集型 I/O密集型

是否真正的并行

多进程 是并行执行,但是否真正并行还取决于物理核数,理论最大并行度= min(进程数, 物理核数)

多线程 在Python中,由于GIL锁的存在,同一时刻只有一个线程能执行Python字节码,所以多线程不存在真正意义上的并行。

协程 协程本身不能实现真正的并行执行,因为单线程内的协程是协作式多任务,非抢占式,同一时刻只有一个协程在执行。

抢占式调度 vs 协作式调度

上面提高了多线程是抢占式调度,和其对应的是协作式调度,下面是两者区别:

特性 抢占式调度(多线程) 协作式调度(协程)
调度层级 操作系统内核层级 应用程序用户态层级
调度主体 操作系统内核 语言运行时/应用程序本身
控制权转移时机 任意时刻(时间片用完、高优先级) 线程显式让出控制权(yield/await等)
响应速度 快(可及时响应高优先级任务) 慢(依赖线程配合)
切换代价 高(需内核接入) 极低(纯用户操作)
系统复杂度 高(需处理中断、上下文保存)
典型应用 通用操作系统(Windows/Linux) 早期系统(Windows 3.x)、协程
公平性 较好(时间片轮转) 依赖线程自觉
确定性 非确定性 确定性
同步 需要锁以同步 不需要同步

3. 深入分析

全局解释器锁(GIL)的影响

由于Python解释器并不是完全线程安全的,所以为了支持多线程,诞生了全局解释锁 GIL。CPython解释器通过GIL确保在同一时刻只有一个线程在执行Python字节码。Python官方文档指出,执行一定量字节码、时间片耗尽(默认5ms)或线程执行IO操作时,GIL会被释放,触发线程切换。Python的多线程是原生操作系统线程,创建和切换都是操作系统级的,在操作系统角度,仍然会给多线程分配多核,且认为Python使用了多核,但实际上由于线程需要先获取GIL才能执行,所以只有一个核心在实际运行,其他核心都是等待GIL的状态,即Python多线程无法利用多核。

Python 3.13开始,可以在编译Python时加入 --disable-gil 参数来禁用 GIL,同时在运行Python程序时,指定 -X gil=0 或设置 PYTHON_GIL=0 环境变量来启用多线程利用多核性能的功能(详见:PEP 703)。

协程更轻量化,可以支持更多并发,且由于是协作式调度(主动让出调度权),不会被意外打断,所以不需要锁用来同步,更高效。 不过当协程与多线程/多进程混合使用:如果协程与线程共享数据,仍需锁(如 asyncio.Lock)。

其他语言的Python实现

特性 PyPy JPython IronPython Stackless Python
介绍 自举实现 Java实现 C#实现 CPython变体
是否有GIL
多线程并行能力 单核(STM 除外) 多核 多核 单核(但高并发)
优化 JIT 编译优化性能 线程安全由 JVM 保证 线程安全由 .NET 运行时保证 通过协程(tasklets)支持高并发
缺点 内存管理仍依赖 GIL 不支持 CPython C 扩展 不支持 CPython C 扩展 仍受限于单核 CPU 计算

性能特点

CPU密集型任务: 多进程 > 协程 ≈ 多线程

I/O密集型任务: 协程 > 多线程 > 多进程

编程复杂度

多线程: 中等,需处理线程安全问题

多进程: 较高,需处理进程间通信

协程: 较低(使用async/await语法),但需要理解事件循环

4. 代码示例比较

多线程示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import threading

def worker():
    print("Thread working")

threads = []
for _ in range(5):
    t = threading.Thread(target=worker)
    threads.append(t)
    t.start()

for t in threads:
    t.join()

多进程示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import multiprocessing

def worker():
    print("Process working")

processes = []
for _ in range(5):
    p = multiprocessing.Process(target=worker)
    processes.append(p)
    p.start()

for p in processes:
    p.join()

协程示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import asyncio

async def worker():
    print("Coroutine working")

async def main():
    tasks = [asyncio.create_task(worker()) for _ in range(5)]
    await asyncio.gather(*tasks)

asyncio.run(main())

5. 混合使用

在实际项目中,可以组合使用这些技术:

  • 多进程 + 多线程:每个进程包含多个线程
  • 多进程 + 协程:每个进程运行一个事件循环
  • 线程池 + 协程:在部分阻塞操作上使用线程池

选择哪种并发方式取决于具体应用场景、性能需求和开发团队的熟悉程度。

updatedupdated2026-02-052026-02-05