Python 自由线程实验性支持

从 3.13 版本开始,CPython 对一种称为自由线程的 Python 构建版本提供了实验性支持,该版本禁用了全局解释器锁 (GIL)。自由线程执行允许通过在可用 CPU 核心上并行运行线程来充分利用可用的处理能力。虽然并非所有软件都能自动从中受益,但考虑到线程设计的程序将在多核硬件上运行得更快。

自由线程模式是实验性的,并且正在进行改进工作:请预期一些错误以及单线程性能的显著下降。

本文档描述了自由线程对 Python 代码的影响。有关如何编写支持自由线程构建的 C 扩展的信息,请参阅自由线程的 C API 扩展支持

另请参阅

PEP 703 – 使 CPython 中的全局解释器锁成为可选,其中对自由线程 Python 进行了总体描述。

安装

从 Python 3.13 开始,官方 macOS 和 Windows 安装程序可选地支持安装自由线程 Python 二进制文件。安装程序可在https://pythonlang.cn/downloads/上找到。

有关其他平台的信息,请参阅安装自由线程 Python,这是一个由社区维护的安装自由线程 Python 的指南。

从源代码构建 CPython 时,应使用--disable-gil配置选项来构建自由线程 Python 解释器。

识别自由线程 Python

要检查当前解释器是否支持自由线程,python -VVsys.version包含“experimental free-threading build”。新的sys._is_gil_enabled()函数可用于检查正在运行的进程中是否实际禁用了 GIL。

sysconfig.get_config_var("Py_GIL_DISABLED")配置变量可用于确定构建是否支持自由线程。如果变量设置为1,则构建支持自由线程。这是与构建配置相关的决策的推荐机制。

自由线程 Python 中的全局解释器锁

CPython 的自由线程构建支持在运行时使用环境变量PYTHON_GIL或命令行选项-X gil选择启用 GIL。

当导入未显式标记为支持自由线程的 C-API 扩展模块时,GIL 也可能会自动启用。在这种情况下,将打印警告。

除了各个软件包的文档之外,以下网站还跟踪受欢迎的软件包对自由线程的支持状态

线程安全

CPython 的自由线程构建旨在在 Python 级别提供与默认启用 GIL 的构建类似的线程安全行为。诸如dictlistset之类的内置类型使用内部锁来防止并发修改,其行为方式与 GIL 类似。但是,Python 历来没有保证对这些内置类型的并发修改的特定行为,因此这应被视为当前实现的描述,而不是对当前或未来行为的保证。

注意

建议尽可能使用threading.Lock或其他同步原语,而不是依赖内置类型的内部锁。

已知限制

本节描述了自由线程 CPython 构建的已知限制。

永生化

3.13 版本的自由线程构建使一些对象永生化。永生对象不会被解除分配,并且其引用计数永远不会被修改。这样做是为了避免引用计数争用,而这会阻止高效的多线程扩展。

在主线程运行后第一次启动新线程时,对象将被永生化。以下对象将被永生化

由于永生对象永远不会被解除分配,因此创建这些类型对象的许多应用程序可能会看到内存使用量增加。预计将在 3.14 版本中解决此问题。

此外,代码中的数字和字符串字面量以及sys.intern()返回的字符串也被永生化。预计此行为将保留在 3.14 的自由线程构建中。

帧对象

从其他线程访问对象是不安全的,这样做可能会导致程序崩溃。这意味着sys._current_frames()在自由线程构建中通常不安全。诸如inspect.currentframe()sys._getframe()之类的函数通常是安全的,前提是不将生成的帧对象传递给另一个线程。

迭代器

在多个线程之间共享同一个迭代器对象通常是不安全的,并且线程在迭代时可能会看到重复或丢失的元素,或者使解释器崩溃。

单线程性能

与默认的启用 GIL 的构建相比,自由线程构建在执行 Python 代码时具有额外的开销。在 3.13 中,pyperformance套件上的开销约为 40%。在 C 扩展或 I/O 中花费大部分时间的程序将看到较小的影响。最大的影响是因为在自由线程构建中禁用了专门的自适应解释器(PEP 659)。我们希望在 3.14 版本中以线程安全的方式重新启用它。预计在即将发布的 Python 版本中,这种开销将会减少。我们的目标是,与默认的启用 GIL 的构建相比,pyperformance 套件上的开销为 10% 或更少。