主页
文章
分类
标签
关于
Python 执行模型与并发体系
发布于: 2025-12-1   更新于: 2025-12-1   收录于: Python
文章字数: 1325   阅读时间: 3 分钟   阅读量:

一、Python 是如何运行的 — 从源码到执行

解释型语言 vs 编译型语言

  • 编译型(如 C/C++):源码 → 一次性编译为机器码 → 直接运行。
  • 解释型(如 Python/JS):源码 → 边解释边执行,但 Python 实际是“先编译成字节码,再解释执行”。

Python 的执行流程

1
2
3
4
5
your_code.py 
    ↓(CPython 解释器编译)
Python 字节码(.pyc,存于 __pycache__)
    ↓(由 Python 虚拟机 PVM 执行)
逐条执行字节码指令

关键点:

  • 字节码:Python 源码的中间表示,平台无关。
  • PVM:不是独立程序,而是 CPython 内部的一个大循环,负责逐条解释执行字节码。
  • .pyc 文件:缓存字节码,避免重复编译,提升启动速度。

二、CPython 与 GIL - 为什么多线程不能并行

什么是 GIL

  • GIL(Global Interpreter Lock) 是 CPython 解释器内部的一个互斥锁。
  • 作用:确保同一时刻只有一个线程在执行 Python 字节码。
  • 目的:保护 CPython 的引用计数内存管理机制,避免多线程并发修改对象导致崩溃。

注意:

  • GIL 是 CPython 特有(PyPy/Jython 无 GIL)。
  • GIL 锁的是 “执行字节码”,不是整个程序。

GIL 如何工作

  • 线程要执行 Python 字节码 → 必须先获取 GIL。
  • 遇到以下情况会释放 GIL:
    • I/O 操作(如 requests.get, time.sleep, 文件读写)
    • 执行 C 扩展(如 NumPy、Pillow)
    • 每执行一定字节码指令后主动让出(时间片切换)

三、并发模型:进程/线程/协程

层级关系

1
2
3
4
操作系统
└── 进程(Process)          ← 每个 `python xxx.py` 是一个独立进程
    └── 线程(Thread)       ← 每个进程至少有 1 个主线程,可创建多个
        └── 协程(Coroutine)← 运行在**单个线程内**,由事件循环调度(如 asyncio)

包含关系:协程 属于 线程 属于 进程 属于 操作系统

一个 Python 程序的默认结构

  • 运行 python main.py → 启动 1 个进程 + 1 个主线程。
  • 在主线程中,可创建:
    • 多个线程(threading.Thread)→ 但受 GIL 限制,不能并行执行 Python 代码
    • 多个协程(async def + await)→ 在单线程内并发,无 GIL 争抢

多进程/多线程/协程

方式 是否绕过 GIL 适用场景 并发/并行 开销
多进程 CPU 密集型 真正并行 高(进程切换)
多线程 I/O 密集型 并发 中(GIL 切换)
协程 单线程) I/O 密集型 并发 极低

实践建议:

  • Web 后端(FastAPI/aiohttp)→ 用 协程(async/await)
  • 科学计算/图像处理 → 用 多进程(multiprocessing)
  • 调用老式阻塞库 → 用 线程池(如 asyncio.to_thread)

四、同步/异步/阻塞/非阻塞

同步Synchronous/异步Asynchronous

  • data = requests.get(url) # 阻塞直到返回

  • data = await httpx.get(url) # 不阻塞,可去做别的事

异步的核心:不无脑等待阻塞,提高资源利用率。

阻塞Blocking/非阻塞Non-blocking

  • 阻塞:调用后必须等待操作完成才返回(如默认的 socket.recv())。
  • 非阻塞:调用立即返回,无论是否准备好(如设置 socket.setblocking(False))。

关键联系: - 异步编程 依赖非阻塞 I/O。 - 同步编程 通常使用阻塞 I/O。

五、总结

  1. Python 程序运行 = 源码 → 字节码 → PVM 执行
  2. CPython 用 GIL 保护字节码执行,导致多线程不能并行 Python 代码
  3. 一个 python xxx.py = 1 进程 + 1 主线程(可有多个协程)
  4. 多进程 = 真正并行(绕过 GIL),适合 CPU 密集
  5. 协程 = 单线程内高效并发,适合 I/O 密集(如 FastAPI)
  6. 异步 = 非阻塞 + 事件循环,最大化 I/O 利用率