菜单

解释器状态与GIL

相关源文件

本文档解释了 CPython 中的解释器状态和全局解释器锁 (GIL),它们构成了 Python 并发模型的基础。解释器状态维护着一个 Python 解释器实例的全局状态,而 GIL 通过一次只允许一个线程执行 Python 字节码来确保线程安全。有关 Python 3.13 中引入的更近期的自由线程模式的信息,请参阅本文档的最后一部分。

解释器状态和 GIL 概述

解释器状态 (PyInterpreterState) 是一个结构,用于维护 Python 解释器实例的全局状态。它管理模块、内置函数、线程状态以及其他解释器范围内的资源。CPython 可以在单个进程中支持多个解释器——每个解释器都有自己的全局状态。

全局解释器锁 (GIL) 是一个互斥锁,可防止多个原生线程同时执行 Python 字节码。它是保护 CPython 内存管理和对象系统免受竞态条件影响的机制。在任何给定时间,只有一个线程可以持有 GIL,这意味着即使在多核系统上,CPython 的多线程也仅限于 Python 代码的并发执行,而非并行执行。

来源:Python/pystate.c Python/ceval_gil.c Include/internal/pycore_interp.h Include/internal/pycore_runtime.h

解释器状态结构

解释器状态代表一个单一的 Python 解释器,其中包含需要在该解释器内的所有线程之间可见的全局状态。CPython 将每个解释器的状态保存在 PyInterpreterState 结构中。

关键组件

PyInterpreterState 结构包括:

组件描述
id解释器的唯一标识符
runtime指向运行时状态的引用
threads属于此解释器的线程状态
builtins内置对象字典
sysdict系统模块字典
modules已导入模块的字典
gc垃圾回收状态
ceval求值和 GIL 相关状态
imports导入相关状态
feature_flags功能标志设置(线程、fork 等)
obmalloc内存分配器状态

与运行时的关系

运行时状态 (_PyRuntimeState) 是一个全局结构,其中包含所有 Python 解释器、线程状态信息和全局锁。主解释器是一个特殊的解释器实例,它在 Python 启动时自动创建,并通过运行时状态访问。

来源:Python/pystate.c585-704 Include/internal/pycore_interp.h Include/internal/pycore_runtime_init.h122-154

全局解释器锁 (GIL)

GIL 是一种互斥锁,在任何给定时间只允许一个线程持有 Python 解释器的控制权。它的主要目的是简化内存管理并避免 CPython 引用计数系统中的竞态条件。

实现细节

CPython 中的 GIL 实现包括:

  • 用于独占访问的互斥锁 (gil->mutex)
  • 用于信号量的条件变量 (gil->cond)
  • 用于跟踪锁状态的标志 (gil->locked)
  • 持有锁的最后一个线程的引用 (gil->last_holder)
  • 线程切换的时间间隔设置

来源:Python/ceval_gil.c147-191 Python/ceval_gil.c203-254

GIL 获取与释放

核心的 GIL 操作包括:

  1. 获取 GIL (take_gil)

    • 等待锁可用
    • 将当前线程设置为锁持有者
    • 更新线程状态,标记其持有 GIL
  2. 释放 GIL (drop_gil)

    • 释放锁
    • 向等待的线程发出 GIL 可用的信号
    • 处理线程切换请求

来源:Python/ceval_gil.c284-360 Python/ceval_gil.c203-254

线程状态

线程状态 (PyThreadState) 负责跟踪 Python 解释器内的线程特定数据。与 Python 交互的每个线程都有自己的线程状态结构。

关键组件

线程状态包括:

组件描述
interp指向解释器状态的引用
next/prev线程状态的双向链表
holds_gil指示线程是否持有 GIL 的标志
current_frame指向当前执行帧的指针
state线程状态(已连接、已断开、已挂起)
py_recursion_remaining递归限制控制
tracing跟踪/性能分析状态
c_profilefuncC 性能分析函数
c_tracefuncC 跟踪函数
eval_breaker用于中断解释器循环的标志

线程状态生命周期

线程状态会经历几个阶段:

  1. 创建:分配并初始化线程状态
  2. 绑定:将线程状态与操作系统线程关联
  3. 附加:将线程标记为活动状态,并可能持有 GIL
  4. 分离:释放 GIL 并将线程标记为非活动状态
  5. 删除:释放线程状态资源

来源:Python/pystate.c250-301 Include/cpython/pystate.h66-186 Include/internal/pycore_pystate.h44-47

解释器生命周期

Python 解释器的生命周期包括创建、初始化和最终化阶段。

初始化

  1. 创建:分配一个新的 PyInterpreterState 结构
  2. 初始化:设置解释器的状态,包括:
    • 设置解释器 ID
    • 链接到运行时状态
    • 初始化求值状态、GC 和类型系统
    • 设置功能标志
  3. 第一个线程:创建主线程并将其绑定到解释器
  4. GIL 创建:为解释器创建 GIL

最终化

当不再需要解释器时,它会经过一个最终化过程:

  1. 清除线程状态:清除所有线程状态
  2. 清除内存:释放模块字典、内置函数和其他资源
  3. 最终 GC 收集:执行最终的垃圾回收
  4. 释放资源:释放解释器结构本身

来源:Python/pystate.c615-704 Python/pystate.c812-947 Python/pylifecycle.c611-677

Python 3.13 的自由线程模式

Python 3.13 引入了对自由线程模式的实验性支持,在这种模式下可以禁用 GIL。这允许在多个 CPU 核心上真正并行执行 Python 代码。

实现

自由线程模式带来了重大变化:

  1. 可选 GIL:可以在构建时使用 --disable-gil 禁用 GIL
  2. 线程安全对象:通过每个对象锁实现 Python 对象的线程安全
  3. 引用计数:使用更复杂的引用计数系统,带有本地和共享计数器

启用自由线程模式

自由线程模式:

  • 默认不启用
  • 需要特殊构建的 Python(通常命名为 python3.13t
  • 可以使用 --disable-gil 选项从源代码构建
  • 是实验性的,可能会导致单线程代码的性能回归

兼容性考虑因素

C 扩展在自由线程模式下需要特殊处理

  • 扩展需要专门为自由线程构建
  • 它们必须使用 Py_mod_gil 插槽来指示 GIL 支持
  • 使用 PyUnstable_Module_SetGIL 的单阶段初始化扩展

来源:Doc/whatsnew/3.13.rst306-352 Include/object.h147-163 Python/pystate.c260-299

结论

解释器状态和 GIL 是 CPython 执行模型的基础。解释器状态维护着 Python 代码执行的全局资源和上下文,而 GIL 以牺牲并行执行为代价,确保了线程安全。随着 Python 3.13 中引入的自由线程模式,CPython 正在开始探索 GIL 的替代方案,这有望在未来实现真正的并行性,尽管目前这仍然是实验性的。

对于 Python 扩展开发者来说,理解这些组件至关重要,特别是那些需要处理线程或开发需要与解释器进行低级交互的模块的开发者。