初始化、终结和线程

另请参见 Python 初始化配置.

Python 初始化之前

在嵌入 Python 的应用程序中,必须在使用任何其他 Python/C API 函数之前调用 Py_Initialize() 函数;除了少数几个函数和 全局配置变量

以下函数可以在 Python 初始化之前安全调用

全局配置变量

Python 具有用于全局配置的变量,以控制不同的功能和选项。默认情况下,这些标志由 命令行选项 控制。

当标志由选项设置时,标志的值是设置选项的次数。例如,-bPy_BytesWarningFlag 设置为 1,而 -bbPy_BytesWarningFlag 设置为 2。

int Py_BytesWarningFlag

此 API 保持向后兼容性:应改为设置 PyConfig.bytes_warning,请参见 Python 初始化配置

在比较 bytesbytearraystrbytesint 时发出警告。如果大于或等于 2,则发出错误。

-b 选项设置。

自 3.12 版本起已弃用。

int Py_DebugFlag

此 API 保持向后兼容性:应改为设置 PyConfig.parser_debug,请参见 Python 初始化配置

打开解析器调试输出(仅供专家使用,取决于编译选项)。

-d 选项和 PYTHONDEBUG 环境变量设置。

自 3.12 版本起已弃用。

int Py_DontWriteBytecodeFlag

此 API 保持向后兼容性:应改为设置 PyConfig.write_bytecode,请参见 Python 初始化配置

如果设置为非零值,Python 不会尝试在导入源模块时写入 .pyc 文件。

-B 选项和 PYTHONDONTWRITEBYTECODE 环境变量设置。

自 3.12 版本起已弃用。

int Py_FrozenFlag

此 API 保持向后兼容性:应改为设置 PyConfig.pathconfig_warnings,请参见 Python 初始化配置

Py_GetPath() 中计算模块搜索路径时,抑制错误消息。

_freeze_modulefrozenmain 程序使用的私有标志。

自 3.12 版本起已弃用。

int Py_HashRandomizationFlag

此 API 保持向后兼容性:应改为设置 PyConfig.hash_seedPyConfig.use_hash_seed,请参见 Python 初始化配置

如果 PYTHONHASHSEED 环境变量设置为非空字符串,则设置为 1

如果该标志为非零值,则读取 PYTHONHASHSEED 环境变量以初始化秘密哈希种子。

自 3.12 版本起已弃用。

int Py_IgnoreEnvironmentFlag

此 API 保持向后兼容性:应改为设置 PyConfig.use_environment,请参阅 Python 初始化配置

忽略所有可能设置的 PYTHON* 环境变量,例如 PYTHONPATHPYTHONHOME

-E-I 选项设置。

自 3.12 版本起已弃用。

int Py_InspectFlag

此 API 保持向后兼容性:应改为设置 PyConfig.inspect,请参阅 Python 初始化配置

当脚本作为第一个参数传递或使用 -c 选项时,即使 sys.stdin 似乎不是终端,也会在执行脚本或命令后进入交互模式。

-i 选项和 PYTHONINSPECT 环境变量设置。

自 3.12 版本起已弃用。

int Py_InteractiveFlag

此 API 保持向后兼容性:应改为设置 PyConfig.interactive,请参阅 Python 初始化配置

-i 选项设置。

自 3.12 版本起已弃用。

int Py_IsolatedFlag

此 API 保持向后兼容性:应改为设置 PyConfig.isolated,请参阅 Python 初始化配置

在隔离模式下运行 Python。在隔离模式下,sys.path 不包含脚本的目录,也不包含用户的 site-packages 目录。

-I 选项设置。

在 3.4 版本中添加。

自 3.12 版本起已弃用。

int Py_LegacyWindowsFSEncodingFlag

此 API 保持向后兼容性:应改为设置 PyPreConfig.legacy_windows_fs_encoding,请参阅 Python 初始化配置

如果标志非零,则对 文件系统编码和错误处理程序 使用 mbcs 编码和 replace 错误处理程序,而不是 UTF-8 编码和 surrogatepass 错误处理程序。

如果 PYTHONLEGACYWINDOWSFSENCODING 环境变量被设置为非空字符串,则设置为 1

有关更多详细信息,请参阅 PEP 529

可用性: Windows。

自 3.12 版本起已弃用。

int Py_LegacyWindowsStdioFlag

此 API 保持向后兼容性:应改为设置 PyConfig.legacy_windows_stdio,请参阅 Python 初始化配置

如果标志非零,则对 sys 标准流使用 io.FileIO 而不是 io._WindowsConsoleIO

如果 PYTHONLEGACYWINDOWSSTDIO 环境变量被设置为非空字符串,则设置为 1

有关更多详细信息,请参阅 PEP 528

可用性: Windows。

自 3.12 版本起已弃用。

int Py_NoSiteFlag

此 API 保持向后兼容性:应改为设置 PyConfig.site_import,请参阅 Python 初始化配置

禁用导入模块 site 以及它所带来的对 sys.path 的依赖于站点的操作。如果 site 稍后被显式导入,也禁用这些操作(如果您希望它们被触发,请调用 site.main())。

-S 选项设置。

自 3.12 版本起已弃用。

int Py_NoUserSiteDirectory

此 API 保持向后兼容性:应改为设置 PyConfig.user_site_directory,请参阅 Python 初始化配置

不要将 user site-packages directory 添加到 sys.path

-s-I 选项以及 PYTHONNOUSERSITE 环境变量设置。

自 3.12 版本起已弃用。

int Py_OptimizeFlag

此 API 保持向后兼容性:应改为设置 PyConfig.optimization_level,请参见 Python 初始化配置

-O 选项和 PYTHONOPTIMIZE 环境变量设置。

自 3.12 版本起已弃用。

int Py_QuietFlag

此 API 保持向后兼容性:应改为设置 PyConfig.quiet,请参见 Python 初始化配置

即使在交互模式下也不显示版权和版本信息。

-q 选项设置。

在版本 3.2 中添加。

自 3.12 版本起已弃用。

int Py_UnbufferedStdioFlag

此 API 保持向后兼容性:应改为设置 PyConfig.buffered_stdio,请参见 Python 初始化配置

强制 stdout 和 stderr 流不带缓冲。

-u 选项和 PYTHONUNBUFFERED 环境变量设置。

自 3.12 版本起已弃用。

int Py_VerboseFlag

此 API 保持向后兼容性:应改为设置 PyConfig.verbose,请参见 Python 初始化配置

每次初始化模块时打印一条消息,显示加载模块的位置(文件名或内置模块)。如果大于或等于 2,则为搜索模块时检查的每个文件打印一条消息。在退出时还提供有关模块清理的信息。

-v 选项和 PYTHONVERBOSE 环境变量设置。

自 3.12 版本起已弃用。

初始化和结束解释器

void Py_Initialize()
稳定 ABI 的一部分。

初始化 Python 解释器。在嵌入 Python 的应用程序中,应在使用任何其他 Python/C API 函数之前调用此函数;有关少数例外情况,请参见 Python 初始化之前

此函数初始化已加载模块的表(sys.modules),并创建基本模块 builtins__main__sys。它还初始化模块搜索路径(sys.path)。它不会设置 sys.argv;请使用 PySys_SetArgvEx() 来完成此操作。如果第二次调用此函数(在没有先调用 Py_FinalizeEx() 的情况下),则此函数为无操作。如果初始化失败,则不会返回值;这是一个致命错误。

使用 Py_InitializeFromConfig() 函数来定制 Python 初始化配置

注意

在 Windows 上,将控制台模式从 O_TEXT 更改为 O_BINARY,这也会影响使用 C 运行时的非 Python 控制台使用。

void Py_InitializeEx(int initsigs)
稳定 ABI 的一部分。

如果 initsigs1,则此函数的工作方式与 Py_Initialize() 相同。如果 initsigs0,则它会跳过信号处理程序的初始化注册,这在嵌入 Python 时可能很有用。

使用 Py_InitializeFromConfig() 函数来定制 Python 初始化配置

int Py_IsInitialized()
稳定 ABI 的一部分。

当 Python 解释器已初始化时返回真(非零),否则返回假(零)。在调用 Py_FinalizeEx() 之后,此函数将返回假,直到再次调用 Py_Initialize() 为止。

int Py_FinalizeEx()
自 3.6 版本起,它是 稳定 ABI 的一部分。

撤消 Py_Initialize() 及后续 Python/C API 函数使用所做的所有初始化,并销毁自上次调用 Py_Initialize() 以来创建但尚未销毁的所有子解释器(参见下面的 Py_NewInterpreter())。理想情况下,这将释放 Python 解释器分配的所有内存。如果第二次调用此函数(在没有先再次调用 Py_Initialize() 的情况下),则此函数为无操作。

由于这是 Py_Initialize() 的反向操作,因此它应该在同一个线程中调用,并且同一个解释器处于活动状态。这意味着主线程和主解释器。在 Py_RunMain() 运行时,不应该调用此函数。

通常返回值为 0。如果在最终化(刷新缓冲数据)期间出现错误,则返回 -1

提供此函数的原因有很多。嵌入式应用程序可能希望重新启动 Python,而无需重新启动应用程序本身。从动态加载库(或 DLL)加载 Python 解释器的应用程序可能希望在卸载 DLL 之前释放 Python 分配的所有内存。在应用程序中查找内存泄漏时,开发人员可能希望在退出应用程序之前释放 Python 分配的所有内存。

错误和注意事项: 模块和模块中对象的销毁以随机顺序进行;这可能会导致析构函数(__del__() 方法)在依赖于其他对象(甚至函数)或模块时失败。Python 加载的动态加载扩展模块不会被卸载。Python 解释器分配的小量内存可能不会被释放(如果您发现泄漏,请报告)。对象之间循环引用所占用的内存不会被释放。扩展模块分配的一些内存可能不会被释放。如果扩展模块的初始化例程被调用多次,则某些扩展模块可能无法正常工作;如果应用程序调用 Py_Initialize()Py_FinalizeEx() 多次,则可能会发生这种情况。

引发一个 审计事件 cpython._PySys_ClearAuditHooks,没有参数。

在 3.6 版本中添加。

void Py_Finalize()
稳定 ABI 的一部分。

这是 Py_FinalizeEx() 的向后兼容版本,它忽略返回值。

进程级参数

int Py_SetStandardStreamEncoding(const char *encoding, const char *errors)

此 API 保持向后兼容性:设置 PyConfig.stdio_encodingPyConfig.stdio_errors 应该使用,参见 Python 初始化配置

如果调用此函数,则应在 Py_Initialize() 之前调用此函数。它指定了与标准 IO 一起使用的编码和错误处理,其含义与 str.encode() 中的含义相同。

它覆盖了 PYTHONIOENCODING 值,并允许嵌入式代码在环境变量不起作用时控制 IO 编码。

encoding 和/或 errors 可以是 NULL,以使用 PYTHONIOENCODING 和/或默认值(取决于其他设置)。

请注意,sys.stderr 始终使用“backslashreplace”错误处理程序,无论此设置(或任何其他设置)如何。

如果调用了 Py_FinalizeEx(),则需要再次调用此函数才能影响后续对 Py_Initialize() 的调用。

如果成功,则返回 0,否则返回非零值(例如,在解释器已初始化后调用)。

在 3.4 版本中添加。

自版本 3.11 起已弃用。

void Py_SetProgramName(const wchar_t *name)
稳定 ABI 的一部分。

此 API 保持向后兼容性:应改为设置 PyConfig.program_name,请参阅 Python 初始化配置

如果调用此函数,则应在首次调用 Py_Initialize() 之前调用此函数。它告诉解释器程序的 argv[0] 参数的值(转换为宽字符)传递给 main() 函数。这被 Py_GetPath() 和下面的一些其他函数用于查找相对于解释器可执行文件的 Python 运行时库。默认值为 'python'。该参数应指向静态存储区中的以零结尾的宽字符字符串,其内容在程序执行期间不会更改。Python 解释器中的任何代码都不会更改此存储区的内容。

使用 Py_DecodeLocale() 将字节字符串解码为 wchar_t* 字符串。

自版本 3.11 起已弃用。

wchar_t *Py_GetProgramName()
稳定 ABI 的一部分。

返回使用 Py_SetProgramName() 设置的程序名称,或默认名称。返回的字符串指向静态存储区;调用者不应修改其值。

此函数不应在调用 Py_Initialize() 之前调用,否则它将返回 NULL

版本 3.10 中的更改:现在,如果在调用 Py_Initialize() 之前调用,它将返回 NULL

wchar_t *Py_GetPrefix()
稳定 ABI 的一部分。

返回已安装平台无关文件的前缀。这是根据从使用 Py_SetProgramName() 设置的程序名称和一些环境变量推断出的,通过一系列复杂的规则得出的;例如,如果程序名称是 '/usr/local/bin/python',则前缀为 '/usr/local'。返回的字符串指向静态存储区;调用者不应修改其值。这对应于顶层 Makefile 中的 prefix 变量以及构建时 configure 脚本的 --prefix 参数。该值可供 Python 代码使用,如 sys.prefix。它仅在 Unix 上有用。另请参见下一个函数。

此函数不应在调用 Py_Initialize() 之前调用,否则它将返回 NULL

版本 3.10 中的更改:现在,如果在调用 Py_Initialize() 之前调用,它将返回 NULL

wchar_t *Py_GetExecPrefix()
稳定 ABI 的一部分。

返回已安装平台相关文件的执行前缀。这是根据从使用 Py_SetProgramName() 设置的程序名称和一些环境变量推断出的,通过一系列复杂的规则得出的;例如,如果程序名称是 '/usr/local/bin/python',则执行前缀为 '/usr/local'。返回的字符串指向静态存储区;调用者不应修改其值。这对应于顶层 Makefile 中的 exec_prefix 变量以及构建时 configure 脚本的 --exec-prefix 参数。该值可供 Python 代码使用,如 sys.exec_prefix。它仅在 Unix 上有用。

背景:当平台相关文件(如可执行文件和共享库)安装在不同的目录树中时,执行前缀与前缀不同。在典型的安装中,平台相关文件可能安装在 /usr/local/plat 子树中,而平台无关文件可能安装在 /usr/local 中。

一般来说,平台是硬件和软件系列的组合,例如,运行 Solaris 2.x 操作系统的 Sparc 机器被认为是同一个平台,但运行 Solaris 2.x 的 Intel 机器是另一个平台,运行 Linux 的 Intel 机器又是另一个平台。同一操作系统的不同主要版本通常也构成不同的平台。非 Unix 操作系统的情况有所不同;这些系统上的安装策略差异很大,以至于前缀和执行前缀毫无意义,并被设置为空字符串。请注意,编译后的 Python 字节码文件是平台无关的(但与编译它们的 Python 版本无关!)。

系统管理员将知道如何配置 mountautomount 程序以在平台之间共享 /usr/local,同时让 /usr/local/plat 成为每个平台的不同文件系统。

此函数不应在调用 Py_Initialize() 之前调用,否则它将返回 NULL

版本 3.10 中的更改:现在,如果在调用 Py_Initialize() 之前调用,它将返回 NULL

wchar_t *Py_GetProgramFullPath()
稳定 ABI 的一部分。

返回 Python 可执行文件的完整程序名称;这是从程序名称(由上面的 Py_SetProgramName() 设置)派生默认模块搜索路径的副作用。返回的字符串指向静态存储区;调用者不应修改其值。该值在 Python 代码中可用作 sys.executable

此函数不应在调用 Py_Initialize() 之前调用,否则它将返回 NULL

版本 3.10 中的更改:现在,如果在调用 Py_Initialize() 之前调用,它将返回 NULL

wchar_t *Py_GetPath()
稳定 ABI 的一部分。

返回默认模块搜索路径;这是从程序名称(由上面的 Py_SetProgramName() 设置)和一些环境变量计算得出的。返回的字符串由一系列用平台相关分隔符字符分隔的目录名称组成。分隔符字符在 Unix 和 macOS 上是 ':',在 Windows 上是 ';'。返回的字符串指向静态存储区;调用者不应修改其值。列表 sys.path 在解释器启动时用此值初始化;它可以(通常是)在以后修改以更改加载模块的搜索路径。

此函数不应在调用 Py_Initialize() 之前调用,否则它将返回 NULL

版本 3.10 中的更改:现在,如果在调用 Py_Initialize() 之前调用,它将返回 NULL

void Py_SetPath(const wchar_t*)
自版本 3.7 起,它是 稳定 ABI 的一部分。

此 API 为了向后兼容而保留:设置 PyConfig.module_search_pathsPyConfig.module_search_paths_set 应该改为使用,请参见 Python 初始化配置

设置默认模块搜索路径。如果在调用 Py_Initialize() 之前调用此函数,则 Py_GetPath() 不会尝试计算默认搜索路径,而是使用提供的路径。如果 Python 被具有所有模块位置的完整知识的应用程序嵌入,这将很有用。路径组件应使用平台相关分隔符字符分隔,在 Unix 和 macOS 上是 ':',在 Windows 上是 ';'

这也导致 sys.executable 设置为程序完整路径(参见 Py_GetProgramFullPath()),并且 sys.prefixsys.exec_prefix 为空。在调用 Py_Initialize() 之后,由调用者负责修改这些内容(如果需要)。

使用 Py_DecodeLocale() 将字节字符串解码为 wchar_* 字符串。

路径参数在内部被复制,因此调用者可以在调用完成后释放它。

在 3.8 版本中变更: 现在使用程序完整路径来表示 sys.executable,而不是程序名称。

自版本 3.11 起已弃用。

const char *Py_GetVersion()
稳定 ABI 的一部分。

返回此 Python 解释器的版本。这是一个类似于以下内容的字符串:

"3.0a5+ (py3k:63103M, May 12 2008, 00:53:55) \n[GCC 4.2.3]"

第一个单词(直到第一个空格字符)是当前 Python 版本;前几个字符是主版本号和次版本号,用句点隔开。返回的字符串指向静态存储区;调用者不应修改其值。该值可供 Python 代码使用,作为 sys.version

另请参见 Py_Version 常量。

const char *Py_GetPlatform()
稳定 ABI 的一部分。

返回当前平台的平台标识符。在 Unix 上,它由操作系统的“官方”名称(转换为小写)和主修订号组成;例如,对于 Solaris 2.x(也称为 SunOS 5.x),其值为 'sunos5'。在 macOS 上,它是 'darwin'。在 Windows 上,它是 'win'。返回的字符串指向静态存储区;调用者不应修改其值。该值可供 Python 代码使用,作为 sys.platform

const char *Py_GetCopyright()
稳定 ABI 的一部分。

返回当前 Python 版本的官方版权字符串,例如:

'Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam'

返回的字符串指向静态存储区;调用者不应修改其值。该值可供 Python 代码使用,作为 sys.copyright

const char *Py_GetCompiler()
稳定 ABI 的一部分。

返回用于构建当前 Python 版本的编译器的指示,用方括号括起来,例如:

"[GCC 2.7.2.2]"

返回的字符串指向静态存储区;调用者不应修改其值。该值可供 Python 代码使用,作为变量 sys.version 的一部分。

const char *Py_GetBuildInfo()
稳定 ABI 的一部分。

返回有关当前 Python 解释器实例的序列号、构建日期和时间的相关信息,例如:

"#67, Aug  1 1997, 22:34:28"

返回的字符串指向静态存储区;调用者不应修改其值。该值可供 Python 代码使用,作为变量 sys.version 的一部分。

void PySys_SetArgvEx(int argc, wchar_t **argv, int updatepath)
稳定 ABI 的一部分。

此 API 保持向后兼容性:设置 PyConfig.argvPyConfig.parse_argvPyConfig.safe_path 应该使用,请参见 Python 初始化配置

根据 *argc* 和 *argv* 设置 sys.argv。这些参数类似于传递给程序 main() 函数的参数,区别在于第一个条目应该引用要执行的脚本文件,而不是托管 Python 解释器的可执行文件。如果没有要运行的脚本,*argv* 中的第一个条目可以为空字符串。如果此函数无法初始化 sys.argv,则使用 Py_FatalError() 信号致命条件。

如果 *updatepath* 为零,则此函数仅执行此操作。如果 *updatepath* 非零,则该函数还会根据以下算法修改 sys.path

  • 如果在 argv[0] 中传递了现有脚本的名称,则脚本所在目录的绝对路径将被预先添加到 sys.path 中。

  • 否则(即,如果 *argc* 为 0argv[0] 不指向现有文件名),空字符串将被预先添加到 sys.path 中,这与预先添加当前工作目录 (".") 相同。

使用 Py_DecodeLocale() 将字节字符串解码为 wchar_* 字符串。

另请参见 PyConfig.orig_argvPyConfig.argvPython 初始化配置 成员。

注意

建议将 Python 解释器嵌入到应用程序中以执行单个脚本以外的目的,将 0 作为 *updatepath* 传递,并在需要时自行更新 sys.path。参见 CVE-2008-5983

在 3.1.3 之前的版本中,您可以通过在调用 PySys_SetArgv() 后手动弹出第一个 sys.path 元素来实现相同的效果,例如使用

PyRun_SimpleString("import sys; sys.path.pop(0)\n");

在 3.1.3 版本中添加。

自版本 3.11 起已弃用。

void PySys_SetArgv(int argc, wchar_t **argv)
稳定 ABI 的一部分。

此 API 保持向后兼容性:应改为设置 PyConfig.argvPyConfig.parse_argv,参见 Python 初始化配置

此函数的工作方式类似于 PySys_SetArgvEx(),其中 *updatepath* 设置为 1,除非 python 解释器使用 -I 启动。

使用 Py_DecodeLocale() 将字节字符串解码为 wchar_* 字符串。

另请参见 PyConfig.orig_argvPyConfig.argvPython 初始化配置 成员。

在 3.4 版本中更改: *updatepath* 值取决于 -I

自版本 3.11 起已弃用。

void Py_SetPythonHome(const wchar_t *home)
稳定 ABI 的一部分。

此 API 为了向后兼容性而保留:应改用设置 PyConfig.home,请参阅 Python 初始化配置

设置默认的“home”目录,即标准 Python 库的位置。有关参数字符串的含义,请参阅 PYTHONHOME

该参数应指向静态存储区中的一个以零结尾的字符字符串,其内容在程序执行期间不会改变。Python 解释器中的任何代码都不会更改此存储区的内容。

使用 Py_DecodeLocale() 将字节字符串解码为 wchar_* 字符串。

自版本 3.11 起已弃用。

wchar_t *Py_GetPythonHome()
稳定 ABI 的一部分。

返回默认的“home”,即之前调用 Py_SetPythonHome() 设置的值,或者如果设置了 PYTHONHOME 环境变量,则返回该变量的值。

此函数不应在调用 Py_Initialize() 之前调用,否则它将返回 NULL

版本 3.10 中的更改:现在,如果在调用 Py_Initialize() 之前调用,它将返回 NULL

线程状态和全局解释器锁

Python 解释器并非完全线程安全。为了支持多线程 Python 程序,存在一个全局锁,称为 全局解释器锁GIL,当前线程必须持有该锁才能安全地访问 Python 对象。如果没有锁,即使是最简单的操作也可能在多线程程序中导致问题:例如,当两个线程同时增加同一个对象的引用计数时,引用计数最终可能只增加了一次而不是两次。

因此,存在以下规则:只有获取了 GIL 的线程才能对 Python 对象进行操作或调用 Python/C API 函数。为了模拟执行的并发性,解释器会定期尝试切换线程(请参阅 sys.setswitchinterval())。在可能阻塞的 I/O 操作(如读取或写入文件)周围也会释放锁,以便其他 Python 线程可以在此期间运行。

Python 解释器在称为 PyThreadState 的数据结构中保留一些特定于线程的簿记信息。还有一个全局变量指向当前的 PyThreadState:可以使用 PyThreadState_Get() 检索它。

从扩展代码中释放 GIL

大多数操作 GIL 的扩展代码具有以下简单的结构

Save the thread state in a local variable.
Release the global interpreter lock.
... Do some blocking I/O operation ...
Reacquire the global interpreter lock.
Restore the thread state from the local variable.

这非常常见,因此存在一对宏来简化它

Py_BEGIN_ALLOW_THREADS
... Do some blocking I/O operation ...
Py_END_ALLOW_THREADS

Py_BEGIN_ALLOW_THREADS 打开一个新块并声明一个隐藏的局部变量;宏 Py_END_ALLOW_THREADS 关闭该块。

上面的块扩展为以下代码

PyThreadState *_save;

_save = PyEval_SaveThread();
... Do some blocking I/O operation ...
PyEval_RestoreThread(_save);

以下是这些函数的工作原理:全局解释器锁用于保护指向当前线程状态的指针。在释放锁并保存线程状态时,必须在释放锁之前检索当前线程状态指针(因为另一个线程可能会立即获取锁并将自己的线程状态存储在全局变量中)。相反,在获取锁并恢复线程状态时,必须在存储线程状态指针之前获取锁。

注意

调用系统 I/O 函数是释放 GIL 的最常见用例,但在调用不需要访问 Python 对象的长时间运行计算之前,它也很有用,例如对内存缓冲区进行压缩或加密函数。例如,标准的 zlibhashlib 模块在压缩或哈希数据时会释放 GIL。

非 Python 创建的线程

当使用专用的 Python API(例如 threading 模块)创建线程时,会自动将线程状态与它们关联,因此上面显示的代码是正确的。但是,当从 C 创建线程(例如,由具有自身线程管理的第三方库创建)时,它们不持有 GIL,也没有为它们提供线程状态结构。

如果您需要从这些线程调用 Python 代码(通常这将是上述第三方库提供的回调 API 的一部分),您必须首先通过创建线程状态数据结构将这些线程注册到解释器,然后获取 GIL,最后存储它们的线程状态指针,然后才能开始使用 Python/C API。完成后,您应该重置线程状态指针,释放 GIL,最后释放线程状态数据结构。

The PyGILState_Ensure()PyGILState_Release() 函数会自动完成所有这些操作。从 C 线程调用 Python 的典型习惯用法是

PyGILState_STATE gstate;
gstate = PyGILState_Ensure();

/* Perform Python actions here. */
result = CallSomeFunction();
/* evaluate result or handle exception */

/* Release the thread. No Python API allowed beyond this point. */
PyGILState_Release(gstate);

请注意,PyGILState_* 函数假设只有一个全局解释器(由 Py_Initialize() 自动创建)。Python 支持创建额外的解释器(使用 Py_NewInterpreter()),但混合使用多个解释器和 PyGILState_* API 不受支持。

关于 fork() 的注意事项

关于线程的另一个重要事项是它们在面对 C fork() 调用时的行为。在大多数具有 fork() 的系统上,进程分叉后,只有发出分叉的线程会存在。这对锁的处理方式以及 CPython 运行时中所有存储的状态都有具体的影响。

由于只有“当前”线程保留,这意味着其他线程持有的任何锁将永远不会被释放。Python 通过在 fork 之前获取其内部使用的锁,并在之后释放它们来解决这个问题。此外,它会重置子进程中的任何 锁对象。在扩展或嵌入 Python 时,无法通知 Python 需要在 fork 之前获取或在 fork 之后重置的其他(非 Python)锁。需要使用 pthread_atfork() 等操作系统设施来完成相同的事情。此外,在扩展或嵌入 Python 时,直接调用 fork() 而不是通过 os.fork()(并返回或调用 Python)可能会导致死锁,因为 Python 的内部锁之一被 fork 后已失效的线程持有。 PyOS_AfterFork_Child() 尝试重置必要的锁,但并非总是能够做到。

所有其他线程都消失的事实也意味着 CPython 的运行时状态必须在那里正确清理,这是 os.fork() 所做的。这意味着要完成属于当前解释器的所有其他 PyThreadState 对象以及所有其他 PyInterpreterState 对象。由于这一点以及 “主”解释器 的特殊性质,fork() 应该只在该解释器的“主”线程中调用,CPython 全局运行时最初是在那里初始化的。唯一的例外是如果 exec() 将在之后立即调用。

高级 API

这些是在编写 C 扩展代码或嵌入 Python 解释器时最常用的类型和函数

type PyInterpreterState
作为 有限 API 的一部分(作为不透明结构)。

此数据结构表示由多个协作线程共享的状态。属于同一解释器的线程共享其模块管理和其他一些内部项。此结构中没有公共成员。

属于不同解释器的线程最初不共享任何东西,除了进程状态,例如可用内存、打开的文件描述符等等。全局解释器锁也由所有线程共享,无论它们属于哪个解释器。

type PyThreadState
作为 有限 API 的一部分(作为不透明结构)。

此数据结构表示单个线程的状态。唯一的公共数据成员是

PyInterpreterState *interp

此线程的解释器状态。

void PyEval_InitThreads()
稳定 ABI 的一部分。

已弃用的函数,不执行任何操作。

在 Python 3.6 及更早版本中,此函数创建 GIL(如果它不存在)。

在 3.9 版本中更改: 此函数现在不执行任何操作。

在 3.7 版本中更改: 此函数现在由 Py_Initialize() 调用,因此您不再需要自己调用它。

在 3.2 版本中更改: 此函数现在不能在 Py_Initialize() 之前调用。

自 3.9 版本起已弃用。

int PyEval_ThreadsInitialized()
稳定 ABI 的一部分。

如果 PyEval_InitThreads() 已被调用,则返回一个非零值。此函数可以在不持有 GIL 的情况下调用,因此可用于在单线程运行时避免调用锁定 API。

在 3.7 版本中变更: 现在 GILPy_Initialize() 初始化。

自 3.9 版本起已弃用。

PyThreadState *PyEval_SaveThread()
稳定 ABI 的一部分。

释放全局解释器锁(如果已创建)并将线程状态重置为 NULL,返回之前的线程状态(不为 NULL)。如果锁已创建,则当前线程必须已获取它。

void PyEval_RestoreThread(PyThreadState *tstate)
稳定 ABI 的一部分。

获取全局解释器锁(如果已创建)并将线程状态设置为 tstate,它不能为 NULL。如果锁已创建,则当前线程不得获取它,否则会导致死锁。

注意

当运行时正在完成时,从线程中调用此函数将终止线程,即使线程不是由 Python 创建的。您可以使用 _Py_IsFinalizing()sys.is_finalizing() 来检查解释器是否正在完成,然后再调用此函数以避免不必要的终止。

PyThreadState *PyThreadState_Get()
稳定 ABI 的一部分。

返回当前线程状态。必须持有全局解释器锁。当当前线程状态为 NULL 时,这会发出致命错误(因此调用者无需检查 NULL)。

PyThreadState *PyThreadState_Swap(PyThreadState *tstate)
稳定 ABI 的一部分。

将当前线程状态与参数 tstate 给出的线程状态交换,它可以为 NULL。必须持有全局解释器锁,并且不会释放它。

以下函数使用线程局部存储,与子解释器不兼容

PyGILState_STATE PyGILState_Ensure()
稳定 ABI 的一部分。

确保当前线程已准备好调用 Python C API,无论 Python 的当前状态或全局解释器锁如何。只要每个调用都与对 PyGILState_Release() 的调用相匹配,线程可以根据需要多次调用此函数。通常,只要在 Release() 之前将线程状态恢复到其先前状态,就可以在 PyGILState_Ensure()PyGILState_Release() 调用之间使用其他与线程相关的 API。例如,正常使用 Py_BEGIN_ALLOW_THREADSPy_END_ALLOW_THREADS 宏是可以接受的。

返回值是在调用 PyGILState_Ensure() 时线程状态的不透明“句柄”,必须将其传递给 PyGILState_Release() 以确保 Python 保持在相同状态。即使允许递归调用,这些句柄也 *不能* 共享 - 每次对 PyGILState_Ensure() 的唯一调用都必须保存其对 PyGILState_Release() 的调用的句柄。

当函数返回时,当前线程将持有 GIL 并能够调用任意 Python 代码。失败是致命错误。

注意

当运行时正在完成时,从线程中调用此函数将终止线程,即使线程不是由 Python 创建的。您可以使用 _Py_IsFinalizing()sys.is_finalizing() 来检查解释器是否正在完成,然后再调用此函数以避免不必要的终止。

void PyGILState_Release(PyGILState_STATE)
稳定 ABI 的一部分。

释放之前获取的任何资源。在此调用之后,Python 的状态将与对应 PyGILState_Ensure() 调用之前相同(但通常此状态对调用者来说是未知的,因此使用 GILState API)。

PyGILState_Ensure() 的每次调用都必须在同一线程上与对 PyGILState_Release() 的调用相匹配。

PyThreadState *PyGILState_GetThisThreadState()
稳定 ABI 的一部分。

获取此线程的当前线程状态。如果当前线程没有使用 GILState API,则可能返回 NULL。请注意,即使在主线程上没有进行任何自动线程状态调用,主线程也始终具有这样的线程状态。这主要是一个辅助/诊断函数。

int PyGILState_Check()

如果当前线程持有 GIL,则返回 1,否则返回 0。此函数可以在任何线程的任何时间调用。只有当它已初始化其 Python 线程状态并且当前持有 GIL 时,它才会返回 1。这主要是一个辅助/诊断函数。例如,它在回调上下文中或内存分配函数中很有用,因为知道 GIL 被锁定可以让调用者执行敏感操作或以其他方式表现不同。

在 3.4 版本中添加。

以下宏通常在没有尾随分号的情况下使用;例如,在 Python 源代码分发中查找示例用法。

Py_BEGIN_ALLOW_THREADS
稳定 ABI 的一部分。

此宏展开为 { PyThreadState *_save; _save = PyEval_SaveThread();。请注意,它包含一个左大括号;它必须与后面的 Py_END_ALLOW_THREADS 宏匹配。有关此宏的更多讨论,请参见上文。

Py_END_ALLOW_THREADS
稳定 ABI 的一部分。

此宏展开为 PyEval_RestoreThread(_save); }。请注意,它包含一个右大括号;它必须与前面的 Py_BEGIN_ALLOW_THREADS 宏匹配。有关此宏的更多讨论,请参见上文。

Py_BLOCK_THREADS
稳定 ABI 的一部分。

此宏展开为 PyEval_RestoreThread(_save);:它等效于 Py_END_ALLOW_THREADS,但没有右大括号。

Py_UNBLOCK_THREADS
稳定 ABI 的一部分。

此宏展开为 _save = PyEval_SaveThread();:它等效于 Py_BEGIN_ALLOW_THREADS,但没有左大括号和变量声明。

低级 API

以下所有函数都必须在 Py_Initialize() 之后调用。

在 3.7 版本中变更: Py_Initialize() 现在初始化 GIL

PyInterpreterState *PyInterpreterState_New()
稳定 ABI 的一部分。

创建一个新的解释器状态对象。全局解释器锁不必被持有,但如果需要序列化对该函数的调用,则可以持有它。

引发一个 审计事件 cpython.PyInterpreterState_New,没有参数。

void PyInterpreterState_Clear(PyInterpreterState *interp)
稳定 ABI 的一部分。

重置解释器状态对象中的所有信息。必须持有全局解释器锁。

引发一个 审计事件 cpython.PyInterpreterState_Clear,没有参数。

void PyInterpreterState_Delete(PyInterpreterState *interp)
稳定 ABI 的一部分。

销毁解释器状态对象。无需持有全局解释器锁。解释器状态必须使用先前对 PyInterpreterState_Clear() 的调用进行重置。

PyThreadState *PyThreadState_New(PyInterpreterState *interp)
稳定 ABI 的一部分。

创建一个属于给定解释器对象的新线程状态对象。无需持有全局解释器锁,但如果需要序列化对该函数的调用,则可以持有该锁。

void PyThreadState_Clear(PyThreadState *tstate)
稳定 ABI 的一部分。

重置线程状态对象中的所有信息。必须持有全局解释器锁。

在版本 3.9 中更改: 此函数现在调用 PyThreadState.on_delete 回调。以前,这发生在 PyThreadState_Delete() 中。

void PyThreadState_Delete(PyThreadState *tstate)
稳定 ABI 的一部分。

销毁线程状态对象。无需持有全局解释器锁。线程状态必须使用先前对 PyThreadState_Clear() 的调用进行重置。

void PyThreadState_DeleteCurrent(void)

销毁当前线程状态并释放全局解释器锁。与 PyThreadState_Delete() 一样,无需持有全局解释器锁。线程状态必须使用先前对 PyThreadState_Clear() 的调用进行重置。

PyFrameObject *PyThreadState_GetFrame(PyThreadState *tstate)
自版本 3.10 起,是 稳定 ABI 的一部分。

获取 Python 线程状态 tstate 的当前帧。

返回一个 强引用。如果当前没有执行帧,则返回 NULL

另请参见 PyEval_GetFrame()

tstate 必须不为 NULL

在版本 3.9 中添加。

uint64_t PyThreadState_GetID(PyThreadState *tstate)
自版本 3.10 起,是 稳定 ABI 的一部分。

获取 Python 线程状态 tstate 的唯一线程状态标识符。

tstate 必须不为 NULL

在版本 3.9 中添加。

PyInterpreterState *PyThreadState_GetInterpreter(PyThreadState *tstate)
自版本 3.10 起,是 稳定 ABI 的一部分。

获取 Python 线程状态 tstate 的解释器。

tstate 必须不为 NULL

在版本 3.9 中添加。

void PyThreadState_EnterTracing(PyThreadState *tstate)

暂停 Python 线程状态 tstate 中的跟踪和分析。

使用 PyThreadState_LeaveTracing() 函数恢复它们。

在 3.11 版本中添加。

void PyThreadState_LeaveTracing(PyThreadState *tstate)

恢复 Python 线程状态 tstate 中的跟踪和分析,这些跟踪和分析被 PyThreadState_EnterTracing() 函数暂停。

另请参见 PyEval_SetTrace()PyEval_SetProfile() 函数。

在 3.11 版本中添加。

PyInterpreterState *PyInterpreterState_Get(void)
从 3.9 版本开始是 稳定 ABI 的一部分。

获取当前解释器。

如果当前没有 Python 线程状态或当前没有解释器,则发出致命错误。它不能返回 NULL。

调用者必须持有 GIL。

在版本 3.9 中添加。

int64_t PyInterpreterState_GetID(PyInterpreterState *interp)
自版本 3.7 起,它是 稳定 ABI 的一部分。

返回解释器的唯一 ID。如果在执行此操作时出现任何错误,则返回 -1 并设置错误。

调用者必须持有 GIL。

在 3.7 版本中添加。

PyObject *PyInterpreterState_GetDict(PyInterpreterState *interp)
从 3.8 版本开始是 稳定 ABI 的一部分。

返回一个字典,可以在其中存储特定于解释器的数据。如果此函数返回 NULL,则没有引发异常,调用者应假定没有可用的特定于解释器的字典。

这不是 PyModule_GetState() 的替代品,扩展应该使用它来存储特定于解释器的状态信息。

在 3.8 版本中添加。

typedef PyObject *(*_PyFrameEvalFunction)(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag)

帧评估函数的类型。

throwflag 参数由生成器的 throw() 方法使用:如果非零,则处理当前异常。

在 3.9 版本中更改: 该函数现在接受一个 tstate 参数。

在 3.11 版本中更改: frame 参数从 PyFrameObject* 更改为 _PyInterpreterFrame*

_PyFrameEvalFunction _PyInterpreterState_GetEvalFrameFunc(PyInterpreterState *interp)

获取帧评估函数。

参见 PEP 523 “向 CPython 添加帧评估 API”。

在版本 3.9 中添加。

void _PyInterpreterState_SetEvalFrameFunc(PyInterpreterState *interp, _PyFrameEvalFunction eval_frame)

设置帧评估函数。

参见 PEP 523 “向 CPython 添加帧评估 API”。

在版本 3.9 中添加。

PyObject *PyThreadState_GetDict()
返回值: 借用引用。稳定 ABI 的一部分。

返回一个字典,扩展可以在其中存储特定于线程的状态信息。每个扩展都应该使用一个唯一的键来在字典中存储状态。在没有当前线程状态可用时调用此函数是可以的。如果此函数返回 NULL,则不会引发异常,调用者应该假设没有当前线程状态可用。

int PyThreadState_SetAsyncExc(unsigned long id, PyObject *exc)
稳定 ABI 的一部分。

在某个线程中异步抛出异常。id 参数是目标线程的线程 ID;exc 是要抛出的异常对象。此函数不会窃取对 exc 的任何引用。为了防止简单粗暴的误用,您必须编写自己的 C 扩展来调用此函数。必须在持有 GIL 的情况下调用。返回修改的线程状态数量;通常为 1,但如果找不到线程 ID,则为 0。如果 excNULL,则清除线程的挂起异常(如果有)。此函数不会抛出任何异常。

版本 3.7 中的变更: id 参数的类型从 long 变为 unsigned long.

void PyEval_AcquireThread(PyThreadState *tstate)
稳定 ABI 的一部分。

获取全局解释器锁,并将当前线程状态设置为 tstatetstate 必须不为 NULL。锁必须在之前创建。如果此线程已经拥有锁,则会导致死锁。

注意

当运行时正在完成时,从线程中调用此函数将终止线程,即使线程不是由 Python 创建的。您可以使用 _Py_IsFinalizing()sys.is_finalizing() 来检查解释器是否正在完成,然后再调用此函数以避免不必要的终止。

版本 3.8 中的变更: 更新为与 PyEval_RestoreThread()Py_END_ALLOW_THREADS()PyGILState_Ensure() 一致,并在解释器正在完成时调用时终止当前线程。

PyEval_RestoreThread() 是一个更高级别的函数,它始终可用(即使线程尚未初始化)。

void PyEval_ReleaseThread(PyThreadState *tstate)
稳定 ABI 的一部分。

将当前线程状态重置为 NULL 并释放全局解释器锁。锁必须在之前创建,并且必须由当前线程持有。tstate 参数(必须不为 NULL)仅用于检查它是否代表当前线程状态——如果不是,则会报告致命错误。

PyEval_SaveThread() 是一个更高级别的函数,它始终可用(即使线程尚未初始化)。

void PyEval_AcquireLock()
稳定 ABI 的一部分。

获取全局解释器锁。锁必须在之前创建。如果此线程已经拥有锁,则会导致死锁。

版本 3.2 中已弃用: 此函数不会更新当前线程状态。请改用 PyEval_RestoreThread()PyEval_AcquireThread()

注意

当运行时正在完成时,从线程中调用此函数将终止线程,即使线程不是由 Python 创建的。您可以使用 _Py_IsFinalizing()sys.is_finalizing() 来检查解释器是否正在完成,然后再调用此函数以避免不必要的终止。

版本 3.8 中的变更: 更新为与 PyEval_RestoreThread()Py_END_ALLOW_THREADS()PyGILState_Ensure() 一致,并在解释器正在完成时调用时终止当前线程。

void PyEval_ReleaseLock()
稳定 ABI 的一部分。

释放全局解释器锁。该锁必须在之前创建。

自版本 3.2 起已弃用: 此函数不会更新当前线程状态。请改用 PyEval_SaveThread()PyEval_ReleaseThread()

子解释器支持

虽然在大多数情况下,您只会嵌入一个 Python 解释器,但有些情况下您需要在同一个进程中甚至同一个线程中创建多个独立的解释器。子解释器允许您这样做。

“主”解释器是在运行时初始化时创建的第一个解释器。它通常是进程中唯一的 Python 解释器。与子解释器不同,主解释器具有独特的进程全局职责,例如信号处理。它还负责运行时初始化期间的执行,并且通常是运行时最终化期间的活动解释器。 PyInterpreterState_Main() 函数返回指向其状态的指针。

您可以使用 PyThreadState_Swap() 函数在子解释器之间切换。您可以使用以下函数创建和销毁它们

type PyInterpreterConfig

包含用于配置子解释器的多数参数的结构。其值仅在 Py_NewInterpreterFromConfig() 中使用,并且不会被运行时修改。

在版本 3.12 中添加。

结构字段

int use_main_obmalloc

如果此值为 0,则子解释器将使用自己的“对象”分配器状态。否则它将使用(共享)主解释器的状态。

如果此值为 0,则 check_multi_interp_extensions 必须为 1(非零)。如果此值为 1,则 gil 必须不为 PyInterpreterConfig_OWN_GIL

int allow_fork

如果此值为 0,则运行时将不支持在子解释器当前处于活动状态的任何线程中分叉进程。否则分叉不受限制。

请注意,当禁止分叉时,subprocess 模块仍然有效。

int allow_exec

如果此值为 0,则运行时将不支持在子解释器当前处于活动状态的任何线程中通过 exec(例如 os.execv())替换当前进程。否则 exec 不受限制。

请注意,即使禁用 exec,subprocess 模块仍然可以正常工作。

int allow_threads

如果此值为 0,则子解释器的 threading 模块将不会创建线程。否则,允许创建线程。

int allow_daemon_threads

如果此值为 0,则子解释器的 threading 模块将不会创建守护线程。否则,允许创建守护线程(只要 allow_threads 不为零)。

int check_multi_interp_extensions

如果此值为 0,则所有扩展模块都可以在子解释器当前处于活动状态的任何线程中导入,包括旧版(单阶段初始化)模块。否则,只能导入多阶段初始化扩展模块(参见 PEP 489)。(另请参见 Py_mod_multiple_interpreters。)

如果 use_main_obmalloc0,则此值必须为 1(非零)。

int gil

此值决定子解释器 GIL 的操作方式。它可以是以下值之一:

PyInterpreterConfig_DEFAULT_GIL

使用默认选择(PyInterpreterConfig_SHARED_GIL)。

PyInterpreterConfig_SHARED_GIL

使用(共享)主解释器的 GIL。

PyInterpreterConfig_OWN_GIL

使用子解释器自己的 GIL。

如果此值为 PyInterpreterConfig_OWN_GIL,则 PyInterpreterConfig.use_main_obmalloc 必须为 0

PyStatus Py_NewInterpreterFromConfig(PyThreadState **tstate_p, const PyInterpreterConfig *config)

创建一个新的子解释器。这是一个(几乎)完全独立的环境,用于执行 Python 代码。特别是,新的解释器拥有所有导入模块的独立版本,包括基本模块 builtins__main__sys。已加载模块表 (sys.modules) 和模块搜索路径 (sys.path) 也是独立的。新环境没有 sys.argv 变量。它有新的标准 I/O 流文件对象 sys.stdinsys.stdoutsys.stderr(但是它们引用相同的底层文件描述符)。

给定的 config 控制解释器初始化时的选项。

成功后,tstate_p 将设置为在新的子解释器中创建的第一个线程状态。此线程状态是在当前线程状态中创建的。请注意,不会创建实际的线程;请参阅下面关于线程状态的讨论。如果创建新的解释器失败,tstate_p 将设置为 NULL;不会设置异常,因为异常状态存储在当前线程状态中,并且可能没有当前线程状态。

与所有其他 Python/C API 函数一样,在调用此函数之前必须持有全局解释器锁,并且在返回时仍然持有。同样,在进入时必须设置当前线程状态。成功后,返回的线程状态将设置为当前状态。如果子解释器使用自己的 GIL 创建,则将释放调用解释器的 GIL。当函数返回时,新解释器的 GIL 将由当前线程持有,而先前解释器的 GIL 将在此处保持释放状态。

在版本 3.12 中添加。

当彼此隔离时,子解释器最有效,某些功能受到限制

PyInterpreterConfig config = {
    .use_main_obmalloc = 0,
    .allow_fork = 0,
    .allow_exec = 0,
    .allow_threads = 1,
    .allow_daemon_threads = 0,
    .check_multi_interp_extensions = 1,
    .gil = PyInterpreterConfig_OWN_GIL,
};
PyThreadState *tstate = Py_NewInterpreterFromConfig(&config);

请注意,config 只会被短暂使用,不会被修改。在初始化期间,config 的值会被转换为各种 PyInterpreterState 值。config 的只读副本可能会存储在 PyInterpreterState 上。

扩展模块在(子)解释器之间共享,如下所示

  • 对于使用多阶段初始化的模块,例如 PyModule_FromDefAndSpec(),会为每个解释器创建和初始化一个单独的模块对象。只有 C 级别静态和全局变量在这些模块对象之间共享。

  • 对于使用单阶段初始化的模块,例如 PyModule_Create(),第一次导入特定扩展时,它会正常初始化,并且其模块字典的(浅层)副本会被保存起来。当另一个(子)解释器导入相同的扩展时,会初始化一个新的模块,并用此副本的内容填充它;扩展的 init 函数不会被调用。因此,模块字典中的对象最终会在(子)解释器之间共享,这可能会导致意外行为(请参阅下面的 错误和注意事项)。

    请注意,这与在调用 Py_FinalizeEx()Py_Initialize() 后完全重新初始化解释器时导入扩展的情况不同;在这种情况下,扩展的 initmodule 函数再次被调用。与多阶段初始化一样,这意味着只有 C 级别静态和全局变量在这些模块之间共享。

PyThreadState *Py_NewInterpreter(void)
稳定 ABI 的一部分。

创建一个新的子解释器。这本质上只是对 Py_NewInterpreterFromConfig() 的包装,其配置保留了现有行为。结果是一个非隔离的子解释器,它与主解释器共享 GIL,允许 fork/exec,允许守护线程,并允许单阶段初始化模块。

void Py_EndInterpreter(PyThreadState *tstate)
稳定 ABI 的一部分。

销毁由给定线程状态表示的(子)解释器。给定的线程状态必须是当前线程状态。请参阅下面关于线程状态的讨论。当调用返回时,当前线程状态为 NULL。与该解释器关联的所有线程状态都将被销毁。在调用此函数之前,必须持有目标解释器使用的全局解释器锁。返回时不持有任何 GIL。

Py_FinalizeEx() 将销毁此时尚未显式销毁的所有子解释器。

每个解释器的 GIL

使用 Py_NewInterpreterFromConfig(),您可以创建一个与其他解释器完全隔离的子解释器,包括拥有自己的 GIL。这种隔离的最重要好处是,这样的解释器可以执行 Python 代码,而不会被其他解释器阻塞,也不会阻塞任何其他解释器。因此,单个 Python 进程在运行 Python 代码时可以真正利用多个 CPU 内核。这种隔离还鼓励采用与仅使用线程不同的并发方法。(参见 PEP 554。)

使用隔离的解释器需要警惕,以保持这种隔离。这意味着尤其要确保在没有线程安全保证的情况下不共享任何对象或可变状态。即使是原本不可变的对象(例如 None(1, 5))通常也不能共享,因为引用计数的原因。解决此问题的一种简单但效率较低的方法是在所有使用某些状态(或对象)时使用全局锁。或者,可以使实际上不可变的对象(如整数或字符串)在引用计数的情况下变得安全,方法是使它们“永生”。实际上,对于内置单例、小整数和许多其他内置对象,已经这样做了。

如果您保持隔离,那么您将能够访问适当的多核计算,而不会遇到自由线程带来的复杂性。未能保持隔离将使您面临自由线程的全部后果,包括竞争条件和难以调试的崩溃。

除此之外,使用多个隔离解释器的主要挑战之一是如何在它们之间安全地(不破坏隔离)且高效地进行通信。运行时和标准库目前没有提供任何标准方法来实现这一点。未来的标准库模块将有助于减轻保持隔离的工作量,并提供有效的工具来在解释器之间进行通信(和共享)数据。

在版本 3.12 中添加。

错误和注意事项

由于子解释器(和主解释器)是同一个进程的一部分,因此它们之间的隔离并不完美——例如,使用低级文件操作(如 os.close()),它们可能会(意外地或恶意地)影响彼此的打开文件。由于扩展在(子)解释器之间共享的方式,某些扩展可能无法正常工作;当使用单阶段初始化或(静态)全局变量时,这种情况尤其可能发生。可以将一个子解释器中创建的对象插入另一个(子)解释器的命名空间中;如果可能,应避免这样做。

应特别注意避免在子解释器之间共享用户定义的函数、方法、实例或类,因为此类对象执行的导入操作可能会影响错误的(子)解释器的已加载模块字典。同样重要的是,要避免共享可以从上述对象中访问的对象。

还要注意,将此功能与 PyGILState_* API 结合使用非常微妙,因为这些 API 假设 Python 线程状态与操作系统级线程之间存在双射关系,而这种假设在存在子解释器的情况下会被打破。强烈建议您不要在匹配的 PyGILState_Ensure()PyGILState_Release() 调用对之间切换子解释器。此外,使用这些 API 允许从非 Python 创建的线程调用 Python 代码的扩展(例如 ctypes)在使用子解释器时可能会被破坏。

异步通知

提供了一种机制,可以向主解释器线程发出异步通知。这些通知采用函数指针和 void 指针参数的形式。

int Py_AddPendingCall(int (*func)(void *), void *arg)
稳定 ABI 的一部分。

安排一个函数从主解释器线程调用。成功时,返回 0,并将 func 加入队列以在主线程中调用。失败时,返回 -1,不设置任何异常。

成功加入队列后,func 将最终从主解释器线程调用,参数为 arg。它将以异步方式调用,相对于正常运行的 Python 代码,但满足以下两个条件

func 必须在成功时返回 0,或在失败时返回 -1,并设置异常。func 不会被中断以递归地执行另一个异步通知,但如果释放了全局解释器锁,它仍然可以被中断以切换线程。

此函数不需要当前线程状态来运行,也不需要全局解释器锁。

要在子解释器中调用此函数,调用者必须持有 GIL。否则,函数 func 可能被安排从错误的解释器调用。

警告

这是一个低级函数,仅适用于非常特殊的情况。不能保证 func 会尽快被调用。如果主线程忙于执行系统调用,func 不会在系统调用返回之前被调用。此函数通常不适合从任意 C 线程调用 Python 代码。相反,请使用 PyGILState API

在 3.1 版本中添加。

在 3.9 版本中更改:如果在子解释器中调用此函数,函数 func 现在被安排从子解释器调用,而不是从主解释器调用。每个子解释器现在都有自己的已安排调用列表。

性能分析和跟踪

Python 解释器提供了一些低级支持,用于附加性能分析和执行跟踪工具。这些用于性能分析、调试和代码覆盖率分析工具。

此 C 接口允许性能分析或跟踪代码避免通过 Python 级可调用对象进行调用的开销,而是直接进行 C 函数调用。该功能的基本属性没有改变;该接口允许为每个线程安装跟踪函数,并且报告给跟踪函数的基本事件与以前版本中报告给 Python 级跟踪函数的事件相同。

typedef int (*Py_tracefunc)(PyObject *obj, PyFrameObject *frame, int what, PyObject *arg)

使用 PyEval_SetProfile()PyEval_SetTrace() 注册的跟踪函数的类型。第一个参数是作为 obj 传递给注册函数的对象,frame 是与事件相关的帧对象,what 是以下常量之一:PyTrace_CALLPyTrace_EXCEPTIONPyTrace_LINEPyTrace_RETURNPyTrace_C_CALLPyTrace_C_EXCEPTIONPyTrace_C_RETURNPyTrace_OPCODE,而 arg 则取决于 what 的值。

what 的值

arg 的含义

PyTrace_CALL

始终为 Py_None

PyTrace_EXCEPTION

异常信息,与 sys.exc_info() 返回的信息相同。

PyTrace_LINE

始终为 Py_None

PyTrace_RETURN

返回给调用者的值,如果由异常引起,则为 NULL

PyTrace_C_CALL

正在调用的函数对象。

PyTrace_C_EXCEPTION

正在调用的函数对象。

PyTrace_C_RETURN

正在调用的函数对象。

PyTrace_OPCODE

始终为 Py_None

int PyTrace_CALL

当报告对函数或方法的新调用或进入生成器时,传递给 Py_tracefunc 函数的 what 参数的值。请注意,生成器函数的迭代器创建不会被报告,因为相应的帧中没有控制转移到 Python 字节码。

int PyTrace_EXCEPTION

当引发异常时,传递给 Py_tracefunc 函数的 what 参数的值。在处理任何字节码后,异常在正在执行的帧中被设置,回调函数将使用此值作为 what 来调用。其效果是,当异常传播导致 Python 栈展开时,回调函数将在异常传播时返回到每个帧时被调用。只有跟踪函数会收到这些事件;剖析器不需要它们。

int PyTrace_LINE

当报告行号事件时,作为 what 参数传递给 Py_tracefunc 函数(但不是剖析函数)的值。可以通过在该帧上将 f_trace_lines 设置为 0 来为帧禁用它。

int PyTrace_RETURN

当调用即将返回时,传递给 Py_tracefunc 函数的 what 参数的值。

int PyTrace_C_CALL

当即将调用 C 函数时,传递给 Py_tracefunc 函数的 what 参数的值。

int PyTrace_C_EXCEPTION

当 C 函数引发异常时,传递给 Py_tracefunc 函数的 what 参数的值。

int PyTrace_C_RETURN

当 C 函数返回时,传递给 Py_tracefunc 函数的 what 参数的值。

int PyTrace_OPCODE

当即将执行新的操作码时,传递给 Py_tracefunc 函数(但不是剖析函数)的 what 参数的值。此事件默认情况下不会发出:必须通过将帧上的 f_trace_opcodes 设置为 1 来显式请求它。

void PyEval_SetProfile(Py_tracefunc func, PyObject *obj)

将分析器函数设置为funcobj参数作为第一个参数传递给函数,可以是任何 Python 对象,也可以是NULL。如果分析器函数需要维护状态,则为每个线程使用不同的obj值提供了一个方便且线程安全的存储位置。分析器函数被调用用于所有监控事件,除了PyTrace_LINE PyTrace_OPCODEPyTrace_EXCEPTION

另请参阅sys.setprofile()函数。

调用者必须持有GIL

void PyEval_SetProfileAllThreads(Py_tracefunc func, PyObject *obj)

类似于PyEval_SetProfile(),但它在当前解释器所属的所有正在运行的线程中设置分析器函数,而不是仅在当前线程中设置。

调用者必须持有GIL

PyEval_SetProfile()一样,此函数忽略在所有线程中设置分析器函数时引发的任何异常。

在版本 3.12 中添加。

void PyEval_SetTrace(Py_tracefunc func, PyObject *obj)

将跟踪函数设置为func。这类似于PyEval_SetProfile(),除了跟踪函数确实会接收行号事件和每个操作码事件,但不会接收任何与调用 C 函数对象相关的事件。使用PyEval_SetTrace()注册的任何跟踪函数都不会接收PyTrace_C_CALLPyTrace_C_EXCEPTIONPyTrace_C_RETURN作为what参数的值。

另请参阅sys.settrace()函数。

调用者必须持有GIL

void PyEval_SetTraceAllThreads(Py_tracefunc func, PyObject *obj)

类似于 PyEval_SetTrace(),但它在当前解释器所属的所有正在运行的线程中设置跟踪函数,而不是仅在当前线程上设置。

调用者必须持有GIL

PyEval_SetTrace() 一样,此函数忽略在所有线程中设置跟踪函数时引发的任何异常。

在版本 3.12 中添加。

高级调试器支持

这些函数仅供高级调试工具使用。

PyInterpreterState *PyInterpreterState_Head()

返回所有此类对象列表头部的解释器状态对象。

PyInterpreterState *PyInterpreterState_Main()

返回主解释器状态对象。

PyInterpreterState *PyInterpreterState_Next(PyInterpreterState *interp)

从所有此类对象的列表中返回interp之后的下一个解释器状态对象。

PyThreadState *PyInterpreterState_ThreadHead(PyInterpreterState *interp)

返回与解释器interp关联的线程列表中第一个 PyThreadState 对象的指针。

PyThreadState *PyThreadState_Next(PyThreadState *tstate)

从属于同一 PyInterpreterState 对象的所有此类对象的列表中返回tstate之后的下一个线程状态对象。

线程本地存储支持

Python 解释器为线程本地存储 (TLS) 提供低级支持,它包装底层本机 TLS 实现以支持 Python 级别的线程本地存储 API (threading.local)。CPython C 级 API 与 pthreads 和 Windows 提供的 API 类似:使用线程键和函数为每个线程关联一个 void* 值。

在调用这些函数时,不需要持有 GIL;它们提供自己的锁定。

请注意,Python.h 不包含 TLS API 的声明,您需要包含 pythread.h 才能使用线程本地存储。

注意

这些 API 函数中的任何一个都不会代表 void* 值处理内存管理。您需要自己分配和释放它们。如果 void* 值恰好是 PyObject*,这些函数也不会对它们执行引用计数操作。

线程特定存储 (TSS) API

TSS API 用于取代 CPython 解释器中现有的 TLS API。此 API 使用新的类型 Py_tss_t 而不是 int 来表示线程键。

在 3.7 版本中添加。

另请参阅

“CPython 中线程本地存储的新 C-API” (PEP 539)

type Py_tss_t

此数据结构表示线程键的状态,其定义可能取决于底层 TLS 实现,并且它有一个内部字段表示键的初始化状态。此结构中没有公共成员。

当未定义 Py_LIMITED_API 时,允许通过 Py_tss_NEEDS_INIT 对此类型进行静态分配。

Py_tss_NEEDS_INIT

此宏扩展为 Py_tss_t 变量的初始化器。请注意,此宏不会在 Py_LIMITED_API 中定义。

动态分配

在使用 Py_LIMITED_API 构建的扩展模块中,需要对 Py_tss_t 进行动态分配,因为在构建时无法静态分配此类型,因为其实现是不透明的。

Py_tss_t *PyThread_tss_alloc()
自版本 3.7 起,它是 稳定 ABI 的一部分。

返回一个值,该值与使用 Py_tss_NEEDS_INIT 初始化的值具有相同的状态,或者在动态分配失败的情况下返回 NULL

void PyThread_tss_free(Py_tss_t *key)
自版本 3.7 起,它是 稳定 ABI 的一部分。

释放由 PyThread_tss_alloc() 分配的给定 key,首先调用 PyThread_tss_delete() 以确保任何关联的线程局部变量都已取消分配。如果 key 参数为 NULL,则此操作为无操作。

注意

已释放的键将成为悬空指针。您应该将键重置为 NULL

方法

这些函数的 key 参数不能为 NULL。此外,如果给定的 Py_tss_t 未通过 PyThread_tss_create() 初始化,则 PyThread_tss_set()PyThread_tss_get() 的行为未定义。

int PyThread_tss_is_created(Py_tss_t *key)
自版本 3.7 起,它是 稳定 ABI 的一部分。

如果给定的 Py_tss_t 已由 PyThread_tss_create() 初始化,则返回非零值。

int PyThread_tss_create(Py_tss_t *key)
自版本 3.7 起,它是 稳定 ABI 的一部分。

成功初始化 TSS 密钥时返回零值。如果 *key* 参数指向的值未由 Py_tss_NEEDS_INIT 初始化,则行为未定义。此函数可以对同一个密钥重复调用 - 对已初始化的密钥调用它是一个无操作,并立即返回成功。

void PyThread_tss_delete(Py_tss_t *key)
自版本 3.7 起,它是 稳定 ABI 的一部分。

销毁 TSS 密钥以忘记与所有线程中密钥关联的值,并将密钥的初始化状态更改为未初始化。已销毁的密钥可以通过 PyThread_tss_create() 再次初始化。此函数可以对同一个密钥重复调用 - 对已销毁的密钥调用它是一个无操作。

int PyThread_tss_set(Py_tss_t *key, void *value)
自版本 3.7 起,它是 稳定 ABI 的一部分。

返回零值以指示在当前线程中成功将 void* 值与 TSS 密钥关联。每个线程都有一个将密钥与 void* 值进行区分的映射。

void *PyThread_tss_get(Py_tss_t *key)
自版本 3.7 起,它是 稳定 ABI 的一部分。

返回与当前线程中的 TSS 密钥关联的 void* 值。如果当前线程中没有值与密钥关联,则返回 NULL

线程本地存储 (TLS) API

自版本 3.7 起已弃用: 此 API 已被 线程特定存储 (TSS) API 取代。

注意

此版本的 API 不支持将本机 TLS 密钥定义为无法安全转换为 int 的方式的平台。在这些平台上,PyThread_create_key() 将立即返回失败状态,并且其他 TLS 函数在这些平台上将全部成为无操作。

由于上述兼容性问题,此版本的 API 不应在新的代码中使用。

int PyThread_create_key()
稳定 ABI 的一部分。
void PyThread_delete_key(int key)
稳定 ABI 的一部分。
int PyThread_set_key_value(int key, void *value)
稳定 ABI 的一部分。
void *PyThread_get_key_value(int key)
稳定 ABI 的一部分。
void PyThread_delete_key_value(int key)
稳定 ABI 的一部分。
void PyThread_ReInitTLS()
稳定 ABI 的一部分。