初始化、终结和线程¶
有关如何在初始化前配置解释器的详细信息,请参阅Python 初始化配置。
Python 初始化之前¶
在嵌入 Python 的应用程序中,必须在使用任何其他 Python/C API 函数之前调用 Py_Initialize()
函数;少数函数和全局配置变量除外。
以下函数可以在 Python 初始化之前安全调用:
初始化解释器的函数
配置函数
信息函数
工具
Python 初始化配置中涵盖的状态报告和实用函数
内存分配器
同步
备注
尽管它们与上面列出的一些函数表面上相似,但解释器初始化之前**不应调用**以下函数:Py_EncodeLocale()
、Py_GetPath()
、Py_GetPrefix()
、Py_GetExecPrefix()
、Py_GetProgramFullPath()
、Py_GetPythonHome()
、Py_GetProgramName()
、PyEval_InitThreads()
和 Py_RunMain()
。
全局配置变量¶
Python 具有用于全局配置的变量,以控制不同的特性和选项。默认情况下,这些标志由命令行选项控制。
当通过某个选项设置标志时,标志的值是该选项设置的次数。例如,-b
将 Py_BytesWarningFlag
设置为 1,-bb
将 Py_BytesWarningFlag
设置为 2。
-
int Py_BytesWarningFlag¶
此 API 仅为向后兼容而保留:应改用设置
PyConfig.bytes_warning
,详见Python 初始化配置。当
bytes
或bytearray
与str
比较,或bytes
与int
比较时,发出警告。如果大于或等于2
,则发出错误。由
-b
选项设置。自 3.12 版本起已弃用,将于 3.15 版本中移除。
-
int Py_DebugFlag¶
此 API 仅为向后兼容而保留:应改用设置
PyConfig.parser_debug
,详见Python 初始化配置。开启解析器调试输出(仅供专家使用,取决于编译选项)。
由
-d
选项和PYTHONDEBUG
环境变量设置。自 3.12 版本起已弃用,将于 3.15 版本中移除。
-
int Py_DontWriteBytecodeFlag¶
此 API 仅为向后兼容而保留:应改用设置
PyConfig.write_bytecode
,详见Python 初始化配置。如果设置为非零,Python 将不会在导入源模块时尝试写入
.pyc
文件。由
-B
选项和PYTHONDONTWRITEBYTECODE
环境变量设置。自 3.12 版本起已弃用,将于 3.15 版本中移除。
-
int Py_FrozenFlag¶
此 API 仅为向后兼容而保留:应改用设置
PyConfig.pathconfig_warnings
,详见Python 初始化配置。在
Py_GetPath()
中计算模块搜索路径时抑制错误消息。由
_freeze_module
和frozenmain
程序使用的私有标志。自 3.12 版本起已弃用,将于 3.15 版本中移除。
-
int Py_HashRandomizationFlag¶
此 API 仅为向后兼容而保留:应改用设置
PyConfig.hash_seed
和PyConfig.use_hash_seed
,详见Python 初始化配置。如果
PYTHONHASHSEED
环境变量设置为非空字符串,则设置为1
。如果标志非零,则读取
PYTHONHASHSEED
环境变量以初始化秘密哈希种子。自 3.12 版本起已弃用,将于 3.15 版本中移除。
-
int Py_IgnoreEnvironmentFlag¶
此 API 仅为向后兼容而保留:应改用设置
PyConfig.use_environment
,详见Python 初始化配置。忽略所有可能设置的
PYTHON*
环境变量,例如PYTHONPATH
和PYTHONHOME
。自 3.12 版本起已弃用,将于 3.15 版本中移除。
-
int Py_InspectFlag¶
此 API 仅为向后兼容而保留:应改用设置
PyConfig.inspect
,详见Python 初始化配置。当脚本作为第一个参数传递或使用
-c
选项时,即使sys.stdin
看起来不是终端,在执行脚本或命令后也会进入交互模式。由
-i
选项和PYTHONINSPECT
环境变量设置。自 3.12 版本起已弃用,将于 3.15 版本中移除。
-
int Py_InteractiveFlag¶
此 API 仅为向后兼容而保留:应改用设置
PyConfig.interactive
,详见Python 初始化配置。由
-i
选项设置。自 3.12 版本起已弃用,将于 3.15 版本中移除。
-
int Py_IsolatedFlag¶
此 API 仅为向后兼容而保留:应改用设置
PyConfig.isolated
,详见Python 初始化配置。在隔离模式下运行 Python。在隔离模式下,
sys.path
既不包含脚本目录,也不包含用户 site-packages 目录。由
-I
选项设置。在 3.4 版本加入。
自 3.12 版本起已弃用,将于 3.15 版本中移除。
-
int Py_LegacyWindowsFSEncodingFlag¶
此 API 仅为向后兼容而保留:应改用设置
PyPreConfig.legacy_windows_fs_encoding
,详见Python 初始化配置。如果该标志非零,则对于文件系统编码和错误处理程序,使用
mbcs
编码和replace
错误处理程序,而不是 UTF-8 编码和surrogatepass
错误处理程序。如果
PYTHONLEGACYWINDOWSFSENCODING
环境变量设置为非空字符串,则设置为1
。有关更多详细信息,请参阅 PEP 529。
可用性:Windows。
自 3.12 版本起已弃用,将于 3.15 版本中移除。
-
int Py_LegacyWindowsStdioFlag¶
此 API 仅为向后兼容而保留:应改用设置
PyConfig.legacy_windows_stdio
,详见Python 初始化配置。如果该标志非零,则对于
sys
标准流,使用io.FileIO
而不是io._WindowsConsoleIO
。如果
PYTHONLEGACYWINDOWSSTDIO
环境变量设置为非空字符串,则设置为1
。有关更多详细信息,请参阅 PEP 528。
可用性:Windows。
自 3.12 版本起已弃用,将于 3.15 版本中移除。
-
int Py_NoSiteFlag¶
此 API 仅为向后兼容而保留:应改用设置
PyConfig.site_import
,详见Python 初始化配置。禁用模块
site
的导入以及它所导致的sys.path
的站点相关操作。即使以后显式导入site
,也禁用这些操作(如果希望触发它们,请调用site.main()
)。由
-S
选项设置。自 3.12 版本起已弃用,将于 3.15 版本中移除。
-
int Py_NoUserSiteDirectory¶
此 API 仅为向后兼容而保留:应改用设置
PyConfig.user_site_directory
,详见Python 初始化配置。不将
user site-packages directory
添加到sys.path
。由
-s
和-I
选项以及PYTHONNOUSERSITE
环境变量设置。自 3.12 版本起已弃用,将于 3.15 版本中移除。
-
int Py_OptimizeFlag¶
此 API 仅为向后兼容而保留:应改用设置
PyConfig.optimization_level
,详见Python 初始化配置。由
-O
选项和PYTHONOPTIMIZE
环境变量设置。自 3.12 版本起已弃用,将于 3.15 版本中移除。
-
int Py_QuietFlag¶
此 API 仅为向后兼容而保留:应改用设置
PyConfig.quiet
,详见Python 初始化配置。即使在交互模式下也不显示版权和版本信息。
由
-q
选项设置。在 3.2 版本加入。
自 3.12 版本起已弃用,将于 3.15 版本中移除。
-
int Py_UnbufferedStdioFlag¶
此 API 仅为向后兼容而保留:应改用设置
PyConfig.buffered_stdio
,详见Python 初始化配置。强制标准输出和标准错误流不带缓冲。
由
-u
选项和PYTHONUNBUFFERED
环境变量设置。自 3.12 版本起已弃用,将于 3.15 版本中移除。
-
int Py_VerboseFlag¶
此 API 仅为向后兼容而保留:应改用设置
PyConfig.verbose
,详见Python 初始化配置。每次模块初始化时打印一条消息,显示其加载位置(文件名或内置模块)。如果大于或等于
2
,则在搜索模块时,检查每个文件时都打印一条消息。还提供退出时模块清理的信息。由
-v
选项和PYTHONVERBOSE
环境变量设置。自 3.12 版本起已弃用,将于 3.15 版本中移除。
初始化和终结解释器¶
-
void Py_Initialize()¶
- 作为 稳定 ABI 的一部分。
初始化 Python 解释器。在嵌入 Python 的应用程序中,应在使用任何其他 Python/C API 函数之前调用此函数;少数例外请参阅Python 初始化之前。
此函数初始化已加载模块的表(
sys.modules
),并创建基本模块builtins
、__main__
和sys
。它还初始化模块搜索路径(sys.path
)。它不设置sys.argv
;为此请使用Python 初始化配置 API。第二次调用时(未首先调用Py_FinalizeEx()
),此函数无效。没有返回值;如果初始化失败,则为致命错误。使用
Py_InitializeFromConfig()
来自定义Python 初始化配置。备注
在 Windows 上,将控制台模式从
O_TEXT
更改为O_BINARY
,这也将影响使用 C 运行时对控制台进行的非 Python 使用。
-
void Py_InitializeEx(int initsigs)¶
- 作为 稳定 ABI 的一部分。
如果 *initsigs* 为
1
,此函数与Py_Initialize()
的作用相同。如果 *initsigs* 为0
,则跳过信号处理程序的初始化注册,当 CPython 作为大型应用程序的一部分嵌入时,这可能很有用。
-
PyStatus Py_InitializeFromConfig(const PyConfig *config)¶
根据 使用 PyConfig 初始化 中所述,根据 *config* 配置初始化 Python。
有关预初始化解释器、填充运行时配置结构以及查询返回状态结构的详细信息,请参阅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()
)。第二次调用时(未再次调用Py_Initialize()
),此函数无效。由于此函数与
Py_Initialize()
相反,因此应在具有相同活动解释器的同一线程中调用。这意味着主线程和主解释器。在Py_RunMain()
运行时绝不应调用此函数。通常返回值为
0
。如果终结过程中发生错误(刷新缓冲数据),则返回-1
。请注意,Python 将尽最大努力释放 Python 解释器分配的所有内存。因此,任何 C 扩展都应确保在后续调用
Py_Initialize()
之前正确清理所有先前分配的 PyObject。否则,可能会引入漏洞和不正确的行为。提供此函数有多种原因。嵌入式应用程序可能希望在不重新启动应用程序本身的情况下重新启动 Python。从动态加载库(或 DLL)加载 Python 解释器的应用程序可能希望在卸载 DLL 之前释放 Python 分配的所有内存。在应用程序中查找内存泄漏时,开发人员可能希望在退出应用程序之前释放 Python 分配的所有内存。
错误和注意事项:模块中模块和对象的销毁是随机进行的;这可能导致析构函数(
__del__()
方法)在依赖于其他对象(甚至是函数)或模块时失败。Python 加载的动态加载扩展模块不会被卸载。Python 解释器分配的少量内存可能不会被释放(如果发现泄漏,请报告)。对象之间循环引用中占用的内存不会被释放。所有内部字符串将被解除分配,无论其引用计数如何。扩展模块分配的一些内存可能不会被释放。如果其初始化例程被多次调用,一些扩展可能无法正常工作;如果应用程序多次调用Py_Initialize()
和Py_FinalizeEx()
,就可能发生这种情况。Py_FinalizeEx()
不得在自身内部递归调用。因此,它不得由任何可能作为解释器关闭过程的一部分运行的代码调用,例如atexit
处理程序、对象终结器或在刷新标准输出和标准错误文件时可能运行的任何代码。引发审计事件
cpython._PySys_ClearAuditHooks
,无参数。在 3.6 版本加入。
-
void Py_Finalize()¶
- 作为 稳定 ABI 的一部分。
这是
Py_FinalizeEx()
的向后兼容版本,它忽略返回值。
-
int Py_BytesMain(int argc, char **argv)¶
- 自 3.8 版本以来,作为稳定 ABI 的一部分。
类似于
Py_Main()
,但 *argv* 是一个字节字符串数组,允许调用应用程序将文本解码步骤委托给 CPython 运行时。在 3.8 版本加入。
-
int Py_Main(int argc, wchar_t **argv)¶
- 作为 稳定 ABI 的一部分。
标准解释器的主程序,封装了一个完整的初始化/终结周期,以及用于根据命令行从环境和命令行读取配置设置然后执行
__main__
的附加行为。这适用于希望支持完整的 CPython 命令行界面,而不仅仅是将 Python 运行时嵌入到更大应用程序中的程序。
*argc* 和 *argv* 参数类似于传递给 C 程序
main()
函数的参数,但 *argv* 条目首先使用Py_DecodeLocale()
转换为wchar_t
。还需要注意的是,参数列表条目可能会被修改为指向传递的字符串以外的字符串(但是,参数列表指向的字符串内容不会被修改)。如果参数列表不表示有效的 Python 命令行,则返回值为
2
,否则与Py_RunMain()
相同。就运行时配置部分中记录的 CPython 运行时配置 API 而言(不考虑错误处理),
Py_Main
大致等同于PyConfig config; PyConfig_InitPythonConfig(&config); PyConfig_SetArgv(&config, argc, argv); Py_InitializeFromConfig(&config); PyConfig_Clear(&config); Py_RunMain();
在正常使用中,嵌入式应用程序将**调用此函数**,而不是直接调用
Py_Initialize()
、Py_InitializeEx()
或Py_InitializeFromConfig()
,并且所有设置都将按本文档其他地方所述应用。如果此函数在之前的运行时初始化 API 调用之后调用,那么将更新哪些环境和命令行配置设置将取决于版本(因为它取决于哪些设置在运行时首次初始化后已设置一次后正确支持修改)。
-
int Py_RunMain(void)¶
在完全配置的 CPython 运行时中执行主模块。
执行命令行或配置中指定的命令(
PyConfig.run_command
)、脚本(PyConfig.run_filename
)或模块(PyConfig.run_module
)。如果这些值都未设置,则使用__main__
模块的全局命名空间运行交互式 Python 提示符 (REPL)。如果未设置
PyConfig.inspect
(默认值),则如果解释器正常退出(即没有引发异常),返回值为0
;如果未处理SystemExit
,则为其退出状态;如果发生任何其他未处理的异常,则为1
。如果设置了
PyConfig.inspect
(例如使用-i
选项时),则解释器退出时不会返回,而是继续在交互式 Python 提示符 (REPL) 中执行,使用__main__
模块的全局命名空间。如果解释器因异常退出,则该异常会立即在 REPL 会话中引发。然后,函数返回值由 *REPL 会话*终止的方式决定:0
、1
或SystemExit
的状态,如上所述。此函数在返回之前总是终结 Python 解释器。
有关始终使用
Py_RunMain()
在隔离模式下运行的自定义 Python 的示例,请参阅Python 配置。
-
int PyUnstable_AtExit(PyInterpreterState *interp, void (*func)(void*), void *data)¶
- 这是一个不稳定 API。它可能会在次要版本中未经警告而更改。
为目标解释器 *interp* 注册
atexit
回调。这类似于Py_AtExit()
,但为回调提供了显式解释器和数据指针。必须存在 *interp* 的附加线程状态。
在 3.13 版本加入。
进程范围参数¶
-
void Py_SetProgramName(const wchar_t *name)¶
- 作为 稳定 ABI 的一部分。
此 API 仅为向后兼容而保留:应改用设置
PyConfig.program_name
,详见Python 初始化配置。如果调用此函数,则应在首次调用
Py_Initialize()
之前调用。它告诉解释器程序main()
函数的argv[0]
参数的值(转换为宽字符)。Py_GetPath()
和下面的一些其他函数使用此值来查找相对于解释器可执行文件的 Python 运行时库。默认值为'python'
。参数应指向静态存储中的一个以零结尾的宽字符字符串,其内容在程序执行期间不会改变。Python 解释器中的任何代码都不会改变此存储的内容。使用
Py_DecodeLocale()
解码字节字符串以获取 wchar_t* 字符串。自 3.11 版本起已弃用,将于 3.15 版本中移除。
-
wchar_t *Py_GetProgramName()¶
- 作为 稳定 ABI 的一部分。
返回使用
PyConfig.program_name
设置的程序名称,或默认值。返回的字符串指向静态存储;调用者不应修改其值。此函数不应在
Py_Initialize()
之前调用,否则返回NULL
。3.10 版本中的变化: 现在如果在
Py_Initialize()
之前调用,则返回NULL
。自 3.13 版本起已弃用,将于 3.15 版本中移除: 请改用
PyConfig_Get("executable")
(sys.executable
)。
-
wchar_t *Py_GetPrefix()¶
- 作为 稳定 ABI 的一部分。
返回已安装的平台无关文件的*前缀*。这是通过从使用
PyConfig.program_name
设置的程序名称和一些环境变量得出的复杂规则生成的;例如,如果程序名称是'/usr/local/bin/python'
,则前缀是'/usr/local'
。返回的字符串指向静态存储;调用者不应修改其值。这对应于顶级Makefile
中的 prefix 变量以及构建时 configure 脚本的--prefix
参数。该值作为sys.base_prefix
可用于 Python 代码。它仅在 Unix 上有用。另请参阅下一个函数。此函数不应在
Py_Initialize()
之前调用,否则返回NULL
。3.10 版本中的变化: 现在如果在
Py_Initialize()
之前调用,则返回NULL
。自 3.13 版本起已弃用,将于 3.15 版本中移除: 请改用
PyConfig_Get("base_prefix")
(sys.base_prefix
)。如果需要处理虚拟环境,请使用PyConfig_Get("prefix")
(sys.prefix
)。
-
wchar_t *Py_GetExecPrefix()¶
- 作为 稳定 ABI 的一部分。
返回已安装的平台*相关*文件的*执行前缀*。这是通过从使用
PyConfig.program_name
设置的程序名称和一些环境变量得出的复杂规则生成的;例如,如果程序名称是'/usr/local/bin/python'
,则执行前缀是'/usr/local'
。返回的字符串指向静态存储;调用者不应修改其值。这对应于顶级Makefile
中的 exec_prefix 变量以及构建时 configure 脚本的--exec-prefix
参数。该值作为sys.base_exec_prefix
可用于 Python 代码。它仅在 Unix 上有用。背景:当平台相关文件(例如可执行文件和共享库)安装在不同的目录树中时,执行前缀与前缀不同。在典型的安装中,平台相关文件可能安装在
/usr/local/plat
子树中,而平台无关文件可能安装在/usr/local
中。一般来说,一个平台是硬件和软件系列的组合,例如,运行 Solaris 2.x 操作系统的 Sparc 机器被认为是同一平台,但运行 Solaris 2.x 的 Intel 机器是另一个平台,运行 Linux 的 Intel 机器又是另一个平台。同一操作系统的不同主要版本通常也构成不同的平台。非 Unix 操作系统是另一回事;这些系统上的安装策略如此不同,以前缀和执行前缀都毫无意义,并设置为一个空字符串。请注意,编译后的 Python 字节码文件是平台无关的(但与编译它们的 Python 版本并非无关!)。
系统管理员将知道如何配置 mount 或 automount 程序,以在平台之间共享
/usr/local
,同时使/usr/local/plat
成为每个平台不同的文件系统。此函数不应在
Py_Initialize()
之前调用,否则返回NULL
。3.10 版本中的变化: 现在如果在
Py_Initialize()
之前调用,则返回NULL
。自 3.13 版本起已弃用,将于 3.15 版本中移除: 请改用
PyConfig_Get("base_exec_prefix")
(sys.base_exec_prefix
)。如果需要处理虚拟环境,请使用PyConfig_Get("exec_prefix")
(sys.exec_prefix
)。
-
wchar_t *Py_GetProgramFullPath()¶
- 作为 稳定 ABI 的一部分。
返回 Python 可执行文件的完整程序名称;这是从程序名称(由
PyConfig.program_name
设置)推导默认模块搜索路径的附带结果计算得出的。返回的字符串指向静态存储;调用者不应修改其值。该值作为sys.executable
可用于 Python 代码。此函数不应在
Py_Initialize()
之前调用,否则返回NULL
。3.10 版本中的变化: 现在如果在
Py_Initialize()
之前调用,则返回NULL
。自 3.13 版本起已弃用,将于 3.15 版本中移除: 请改用
PyConfig_Get("executable")
(sys.executable
)。
-
wchar_t *Py_GetPath()¶
- 作为 稳定 ABI 的一部分。
返回默认模块搜索路径;这是从程序名称(由
PyConfig.program_name
设置)和一些环境变量计算得出的。返回的字符串由一系列目录名组成,这些目录名由平台相关的分隔符字符分隔。分隔符字符在 Unix 和 macOS 上是':'
,在 Windows 上是';'
。返回的字符串指向静态存储;调用者不应修改其值。列表sys.path
在解释器启动时使用此值初始化;以后可以(并且通常会)修改它以更改加载模块的搜索路径。此函数不应在
Py_Initialize()
之前调用,否则返回NULL
。3.10 版本中的变化: 现在如果在
Py_Initialize()
之前调用,则返回NULL
。自 3.13 版本起已弃用,将于 3.15 版本中移除: 请改用
PyConfig_Get("module_search_paths")
(sys.path
)。
-
const char *Py_GetVersion()¶
- 作为 稳定 ABI 的一部分。
返回此 Python 解释器的版本。这是一个字符串,看起来像这样:
"3.0a5+ (py3k:63103M, May 12 2008, 00:53:55) \n[GCC 4.2.3]"
第一个单词(直到第一个空格字符)是当前的 Python 版本;第一个字符是主要版本和次要版本,用句点分隔。返回的字符串指向静态存储;调用者不应修改其值。该值作为
sys.version
可用于 Python 代码。另请参阅
Py_Version
常量。
-
const char *Py_GetPlatform()¶
- 作为 稳定 ABI 的一部分。
返回当前平台的平台标识符。在 Unix 上,这是由操作系统的“官方”名称(转换为小写)加上主要修订号组成的;例如,对于 Solaris 2.x(也称为 SunOS 5.x),该值为
'sunos5'
。在 macOS 上,它是'darwin'
。在 Windows 上,它是'win'
。返回的字符串指向静态存储;调用者不应修改其值。该值作为sys.platform
可用于 Python 代码。
-
const char *Py_GetCopyright()¶
- 作为 稳定 ABI 的一部分。
返回当前 Python 版本的官方版权字符串,例如
'Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam'
返回的字符串指向静态存储;调用者不应修改其值。该值作为
sys.copyright
可用于 Python 代码。
-
const char *Py_GetCompiler()¶
- 作为 稳定 ABI 的一部分。
返回用于构建当前 Python 版本的编译器的指示,用方括号括起来,例如
"[GCC 2.7.2.2]"
返回的字符串指向静态存储;调用者不应修改其值。该值作为
sys.version
变量的一部分可用于 Python 代码。
-
const char *Py_GetBuildInfo()¶
- 作为 稳定 ABI 的一部分。
返回有关当前 Python 解释器实例的序列号以及构建日期和时间的信息,例如
"#67, Aug 1 1997, 22:34:28"
返回的字符串指向静态存储;调用者不应修改其值。该值作为
sys.version
变量的一部分可用于 Python 代码。
-
void PySys_SetArgvEx(int argc, wchar_t **argv, int updatepath)¶
- 作为 稳定 ABI 的一部分。
此 API 仅为向后兼容而保留:应改用设置
PyConfig.argv
、PyConfig.parse_argv
和PyConfig.safe_path
,详见Python 初始化配置。根据 *argc* 和 *argv* 设置
sys.argv
。这些参数类似于传递给程序main()
函数的参数,不同之处在于第一个条目应指向要执行的脚本文件,而不是托管 Python 解释器的可执行文件。如果没有要运行的脚本,*argv* 中的第一个条目可以是空字符串。如果此函数无法初始化sys.argv
,则使用Py_FatalError()
发出致命条件信号。如果 *updatepath* 为零,则函数只执行这些操作。如果 *updatepath* 非零,函数还会根据以下算法修改
sys.path
如果
argv[0]
中传递了现有脚本的名称,则脚本所在目录的绝对路径将添加到sys.path
的开头。否则(即,如果 *argc* 为
0
或argv[0]
不指向现有文件名),则将一个空字符串添加到sys.path
的开头,这与添加当前工作目录("."
)相同。
使用
Py_DecodeLocale()
解码字节字符串以获取 wchar_t* 字符串。另请参阅Python 初始化配置的
PyConfig.orig_argv
和PyConfig.argv
成员。备注
建议除了执行单个脚本之外的其他目的嵌入 Python 解释器的应用程序将 *updatepath* 设为
0
,并根据需要自行更新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 版本起已弃用,将于 3.15 版本中移除。
-
void PySys_SetArgv(int argc, wchar_t **argv)¶
- 作为 稳定 ABI 的一部分。
此 API 保留用于向后兼容:应使用设置
PyConfig.argv
和PyConfig.parse_argv
,详见 Python 初始化配置。此函数的工作方式类似于
PySys_SetArgvEx()
,其中 updatepath 设置为1
,除非 python 解释器使用-I
启动。使用
Py_DecodeLocale()
解码字节字符串以获取 wchar_t* 字符串。另请参阅Python 初始化配置的
PyConfig.orig_argv
和PyConfig.argv
成员。3.4 版本中已更改: updatepath 值取决于
-I
。自 3.11 版本起已弃用,将于 3.15 版本中移除。
-
void Py_SetPythonHome(const wchar_t *home)¶
- 作为 稳定 ABI 的一部分。
此 API 保留用于向后兼容:应使用设置
PyConfig.home
,详见 Python 初始化配置。设置默认的“home”目录,即标准 Python 库的位置。有关参数字符串的含义,请参见
PYTHONHOME
。该参数应指向静态存储中的一个以零结尾的字符串,其内容在程序执行期间不会改变。Python 解释器中的任何代码都不会改变此存储的内容。
使用
Py_DecodeLocale()
解码字节字符串以获取 wchar_t* 字符串。自 3.11 版本起已弃用,将于 3.15 版本中移除。
-
wchar_t *Py_GetPythonHome()¶
- 作为 稳定 ABI 的一部分。
返回默认的“home”,即由
PyConfig.home
设置的值,或者如果设置了PYTHONHOME
环境变量,则返回其值。此函数不应在
Py_Initialize()
之前调用,否则返回NULL
。3.10 版本中的变化: 现在如果在
Py_Initialize()
之前调用,则返回NULL
。自 3.13 版本弃用,将于 3.15 版本移除: 请使用
PyConfig_Get("home")
或PYTHONHOME
环境变量。
线程状态和全局解释器锁¶
除非是在自由线程构建的CPython上,Python解释器并非完全线程安全。为了支持多线程Python程序,存在一个全局锁,称为全局解释器锁或GIL,当前线程在安全访问Python对象之前必须持有它。如果没有这个锁,即使是最简单的操作也可能在多线程程序中引起问题:例如,当两个线程同时递增同一对象的引用计数时,引用计数可能只增加一次而不是两次。
因此,存在一条规则,即只有持有 GIL 的线程才能操作 Python 对象或调用 Python/C API 函数。为了模拟执行的并发性,解释器会定期尝试切换线程(参见 sys.setswitchinterval()
)。在可能阻塞的 I/O 操作(如读写文件)周围也会释放锁,以便其他 Python 线程在此期间运行。
Python 解释器将一些线程特定的记账信息保存在一个名为 PyThreadState
的数据结构中,这被称为线程状态。每个操作系统线程都有一个指向 PyThreadState
的线程本地指针;由该指针引用的线程状态被认为是已附加的。
一个线程一次只能有一个已附加线程状态。一个已附加线程状态通常与持有GIL类似,但自由线程构建除外。在启用GIL的构建中,附加线程状态将阻塞,直到可以获取GIL。然而,即使在禁用GIL的构建中,仍然需要有已附加线程状态才能调用大部分C API。
一般来说,在使用 Python 的 C API 时,总会有一个已附加线程状态。只有在某些特定情况下(例如在 Py_BEGIN_ALLOW_THREADS
块中),线程才不会有已附加线程状态。如果不确定,请检查 PyThreadState_GetUnchecked()
是否返回 NULL
。
从扩展代码中分离线程状态¶
大多数操作线程状态的扩展代码具有以下简单结构
Save the thread state in a local variable.
... Do some blocking I/O operation ...
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);
这些函数的工作方式如下
已附加线程状态为整个解释器持有GIL。当分离已附加线程状态时,GIL被释放,允许其他线程将线程状态附加到自己的线程,从而获取GIL并开始执行。指向先前已附加线程状态的指针存储为局部变量。当到达Py_END_ALLOW_THREADS
时,之前已附加的线程状态被传递给PyEval_RestoreThread()
。此函数将阻塞,直到另一个线程释放其线程状态,从而允许旧的线程状态重新附加,并且可以再次调用C API。
对于自由线程构建,GIL通常不在考虑范围内,但对于阻塞I/O和长时间操作仍然需要分离线程状态。不同之处在于,线程不必等待GIL释放即可附加其线程状态,从而实现真正的多核并行性。
非 Python 创建的线程¶
当使用专门的 Python API(例如 threading
模块)创建线程时,线程状态会自动与之关联,因此上面显示的代码是正确的。然而,当从 C 创建线程时(例如由具有自己线程管理的第三方库创建),它们不持有 GIL,因为它们没有已附加线程状态。
如果您需要从这些线程调用 Python 代码(通常这将是上述第三方库提供的回调 API 的一部分),则在开始使用 Python/C API 之前,您必须首先通过创建已附加线程状态来向解释器注册这些线程。完成后,您应该分离线程状态,最后将其释放。
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 是不受支持的。这是因为 PyGILState_Ensure()
和类似函数默认附加主解释器的线程状态,这意味着线程无法安全地与调用子解释器交互。
在非 Python 线程中支持子解释器¶
如果您想在非 Python 创建的线程中支持子解释器,则必须使用 PyThreadState_*
API 而不是传统的 PyGILState_*
API。
特别是,您必须存储来自调用函数的解释器状态并将其传递给 PyThreadState_New()
,这将确保线程状态指向正确的解释器
/* The return value of PyInterpreterState_Get() from the
function that created this thread. */
PyInterpreterState *interp = ThreadData->interp;
PyThreadState *tstate = PyThreadState_New(interp);
PyThreadState_Swap(tstate);
/* GIL of the subinterpreter is now held.
Perform Python actions here. */
result = CallSomeFunction();
/* evaluate result or handle exception */
/* Destroy the thread state. No Python API allowed beyond this point. */
PyThreadState_Clear(tstate);
PyThreadState_DeleteCurrent();
关于 fork() 的注意事项¶
关于线程,另一个需要注意的重要事项是它们在 C fork()
调用面前的行为。在大多数支持 fork()
的系统上,进程 fork 后只剩下发出 fork 的线程。这会对锁的处理方式和 CPython 运行时中所有存储的状态产生具体影响。
只剩下“当前”线程的事实意味着其他线程持有的任何锁将永远不会被释放。Python 针对 os.fork()
解决了这个问题,它在 fork 之前获取内部使用的锁,并在之后释放它们。此外,它会重置子进程中的任何 Lock 对象。在扩展或嵌入 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()
。
关于运行时终结的注意事项¶
在解释器关闭的后期阶段,尝试等待非守护线程退出(尽管这可能会被 KeyboardInterrupt
中断)并运行 atexit
函数之后,运行时被标记为*正在终结*:Py_IsFinalizing()
和 sys.is_finalizing()
返回 true。此时,只有启动终结的*终结线程*(通常是主线程)才允许获取 GIL。
如果任何线程(终结线程除外)在终结期间尝试显式或隐式地附加线程状态,该线程将进入**永久阻塞状态**,直到程序退出。在大多数情况下,这是无害的,但如果终结的后续阶段尝试获取被阻塞线程拥有的锁,或者以其他方式等待被阻塞线程,则可能导致死锁。
糟糕?是的。这可以防止在 CPython 3.13 及更早版本中强制退出此类线程时,调用堆栈中更上层的随机崩溃和/或意外跳过的 C++ 终结。CPython 运行时线程状态 C API 在线程状态附加时从未有过任何错误报告或处理预期,这本可以允许优雅地退出这种情况。改变这一点将需要新的稳定 C API 并重写 CPython 生态系统中大多数 C 代码以使用这些 API 并进行错误处理。
高级 API¶
这些是编写 C 扩展代码或嵌入 Python 解释器时最常用的类型和函数
-
type PyInterpreterState¶
- 受限 API 的一部分(作为不透明结构体)。
此数据结构表示多个协作线程共享的状态。属于同一解释器的线程共享其模块管理和一些其他内部项。此结构中没有公共成员。
属于不同解释器的线程最初不共享任何内容,除了进程状态,例如可用内存、打开的文件描述符等。全局解释器锁也由所有线程共享,无论它们属于哪个解释器。
3.12 版本中已更改: PEP 684 引入了 每解释器 GIL 的可能性。请参阅
Py_NewInterpreterFromConfig()
。
-
type PyThreadState¶
- 受限 API 的一部分(作为不透明结构体)。
此数据结构表示单个线程的状态。唯一的公共数据成员是
-
PyInterpreterState *interp¶
此线程的解释器状态。
-
PyInterpreterState *interp¶
-
void PyEval_InitThreads()¶
- 作为 稳定 ABI 的一部分。
已弃用,不起作用。
在Python 3.6及更早版本中,此函数在GIL不存在时创建GIL。
3.9 版本中已更改: 该函数现在什么也不做。
3.7 版本中已更改: 此函数现在由
Py_Initialize()
调用,因此您不再需要自己调用它。3.2 版本中已更改: 此函数不能再在
Py_Initialize()
之前调用。自 3.9 版本弃用。
-
PyThreadState *PyEval_SaveThread()¶
- 作为 稳定 ABI 的一部分。
-
void PyEval_RestoreThread(PyThreadState *tstate)¶
- 作为 稳定 ABI 的一部分。
将已附加线程状态设置为 tstate。传入的线程状态**不应该**是已附加的,否则会导致死锁。tstate 将在返回时被附加。
备注
在运行时正在终结时从线程调用此函数将使该线程挂起,直到程序退出,即使该线程不是由 Python 创建的。有关更多详细信息,请参阅 关于运行时终结的注意事项。
3.14 版本中已更改: 如果在解释器正在终结时调用,则会挂起当前线程,而不是终止它。
-
PyThreadState *PyThreadState_Get()¶
- 作为 稳定 ABI 的一部分。
返回已附加线程状态。如果线程没有已附加线程状态(例如在
Py_BEGIN_ALLOW_THREADS
块内部时),则会发出致命错误(以便调用者不必检查NULL
)。
-
PyThreadState *PyThreadState_GetUnchecked()¶
与
PyThreadState_Get()
类似,但如果为 NULL 则不会因致命错误而终止进程。调用者负责检查结果是否为 NULL。3.13 版本中新增: 在 Python 3.5 到 3.12 中,该函数是私有的,被称为
_PyThreadState_UncheckedGet()
。
-
PyThreadState *PyThreadState_Swap(PyThreadState *tstate)¶
- 作为 稳定 ABI 的一部分。
将已附加线程状态设置为 tstate,并返回调用前已附加的线程状态。
此函数在没有已附加线程状态的情况下调用是安全的;它只会返回
NULL
,表示没有先前的线程状态。备注
与
PyGILState_Ensure()
类似,如果运行时正在终结,此函数将挂起线程。
以下函数使用线程本地存储,与子解释器不兼容
-
type PyGILState_STATE¶
- 作为 稳定 ABI 的一部分。
PyGILState_Ensure()
返回并传递给PyGILState_Release()
的值的类型。-
enumerator PyGILState_LOCKED¶
调用
PyGILState_Ensure()
时 GIL 已被持有。
-
enumerator PyGILState_UNLOCKED¶
调用
PyGILState_Ensure()
时 GIL 未被持有。
-
enumerator PyGILState_LOCKED¶
-
PyGILState_STATE PyGILState_Ensure()¶
- 作为 稳定 ABI 的一部分。
确保当前线程已准备好调用 Python C API,无论 Python 当前状态或已附加线程状态如何。只要每次调用都与一次
PyGILState_Release()
调用匹配,线程就可以随意多次调用此函数。通常,在PyGILState_Ensure()
和PyGILState_Release()
调用之间可以使用其他线程相关 API,只要在 Release() 之前将线程状态恢复到其先前状态即可。例如,Py_BEGIN_ALLOW_THREADS
和Py_END_ALLOW_THREADS
宏的正常使用是可接受的。返回值是调用
PyGILState_Ensure()
时已附加线程状态的不透明“句柄”,并且必须传递给PyGILState_Release()
以确保 Python 保持在相同的状态。尽管允许递归调用,但这些句柄**不能**共享——每次对PyGILState_Ensure()
的唯一调用都必须为其对PyGILState_Release()
的调用保存句柄。函数返回时,将有一个已附加线程状态,并且线程将能够调用任意 Python 代码。失败是致命错误。
警告
在运行时正在终结时调用此函数是不安全的。这样做要么会使线程挂起直到程序结束,要么在极少数情况下完全崩溃解释器。有关更多详细信息,请参阅 关于运行时终结的注意事项。
3.14 版本中已更改: 如果在解释器正在终结时调用,则会挂起当前线程,而不是终止它。
-
void PyGILState_Release(PyGILState_STATE)¶
- 作为 稳定 ABI 的一部分。
释放先前获取的任何资源。在此调用之后,Python 的状态将与相应
PyGILState_Ensure()
调用之前的状态相同(但通常此状态对调用者未知,因此使用 GILState API)。每次调用
PyGILState_Ensure()
都必须与同一线程上的PyGILState_Release()
调用匹配。
-
PyThreadState *PyGILState_GetThisThreadState()¶
- 作为 稳定 ABI 的一部分。
获取此线程的已附加线程状态。如果当前线程未使用任何 GILState API,则可能返回
NULL
。请注意,主线程始终具有此类线程状态,即使主线程上未进行自动线程状态调用。这主要是辅助/诊断函数。备注
即使线程状态已分离,此函数也可能返回非
NULL
。在大多数情况下,首选PyThreadState_Get()
或PyThreadState_GetUnchecked()
。
-
int PyGILState_Check()¶
如果当前线程持有 GIL,则返回
1
,否则返回0
。此函数可由任何线程在任何时间调用。只有当它的线程状态已通过PyGILState_Ensure()
初始化后,它才会返回1
。这主要是一个辅助/诊断函数。例如,在回调上下文或内存分配函数中,了解 GIL 已锁定可以允许调用者执行敏感操作或以其他方式以不同方式行事时,它可能很有用。备注
如果当前 Python 进程曾经创建过子解释器,此函数将*始终*返回
1
。在大多数情况下,首选PyThreadState_GetUnchecked()
。在 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()
中。3.13 版本中已更改:
PyThreadState.on_delete
回调已移除。
-
void PyThreadState_Delete(PyThreadState *tstate)¶
- 作为 稳定 ABI 的一部分。
销毁一个线程状态对象。tstate 不应附加到任何线程。tstate 必须已通过之前调用
PyThreadState_Clear()
进行重置。
-
void PyThreadState_DeleteCurrent(void)¶
分离已附加线程状态(该状态必须已通过先前调用
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)¶
恢复由
PyThreadState_EnterTracing()
函数暂停的 Python 线程状态 tstate 中的跟踪和分析。另请参阅
PyEval_SetTrace()
和PyEval_SetProfile()
函数。在 3.11 版本中新增。
-
PyInterpreterState *PyInterpreterState_Get(void)¶
- 自 3.9 版本以来成为 稳定 ABI 的一部分。
获取当前解释器。
如果没有已附加线程状态,则会发出致命错误。它不能返回 NULL。
在 3.9 版本中新增。
-
int64_t PyInterpreterState_GetID(PyInterpreterState *interp)¶
- 自 3.7 版本起成为 稳定ABI 的一部分。
返回解释器的唯一 ID。如果在此过程中发生任何错误,则返回
-1
并设置错误。调用者必须具有 已附加的线程状态。
在 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 扩展来调用此函数。必须在附加线程状态下调用。返回已修改的线程状态数量;这通常是一个,但如果找不到线程 ID,则为零。如果 exc 为
NULL
,则清除线程的待处理异常(如果有)。这不会引发任何异常。3.7 版更改: id 参数的类型从 long 更改为 unsigned long。
-
void PyEval_AcquireThread(PyThreadState *tstate)¶
- 作为 稳定 ABI 的一部分。
附加 tstate 到当前线程,该线程不得为
NULL
或已附加。调用线程不得已有附加线程状态。
备注
在运行时正在终结时从线程调用此函数将使该线程挂起,直到程序退出,即使该线程不是由 Python 创建的。有关更多详细信息,请参阅 关于运行时终结的注意事项。
3.8 版更改: 已更新,以与
PyEval_RestoreThread()
、Py_END_ALLOW_THREADS()
和PyGILState_Ensure()
保持一致,并在解释器正在终止时调用时终止当前线程。3.14 版本中已更改: 如果在解释器正在终结时调用,则会挂起当前线程,而不是终止它。
PyEval_RestoreThread()
是一个更高级别的函数,始终可用(即使在线程尚未初始化时)。
-
void PyEval_ReleaseThread(PyThreadState *tstate)¶
- 作为 稳定 ABI 的一部分。
分离附加线程状态。tstate 参数不得为
NULL
,仅用于检查它是否表示附加线程状态 — 如果不是,则报告致命错误。PyEval_SaveThread()
是一个更高级别的函数,始终可用(即使在线程尚未初始化时)。
子解释器支持¶
尽管在大多数使用中,您只嵌入一个 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_daemon_threads¶
如果此值为
0
,则子解释器的threading
模块将不会创建守护线程。否则,允许守护线程(只要allow_threads
为非零)。
-
int check_multi_interp_extensions¶
如果此值为
0
,则所有扩展模块都可以导入,包括旧版(单阶段初始化)模块,在子解释器当前处于活动状态的任何线程中。否则,只能导入多阶段初始化扩展模块(参见PEP 489)。 (另请参见Py_mod_multiple_interpreters
。)如果
use_main_obmalloc
为0
,则此值必须为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
。-
PyInterpreterConfig_DEFAULT_GIL¶
-
int use_main_obmalloc¶
-
PyStatus Py_NewInterpreterFromConfig(PyThreadState **tstate_p, const PyInterpreterConfig *config)¶
创建一个新的子解释器。这是一个(几乎)完全独立的 Python 代码执行环境。特别是,新的解释器拥有所有导入模块的独立版本,包括基本模块
builtins
、__main__
和sys
。已加载模块的表 (sys.modules
) 和模块搜索路径 (sys.path
) 也是独立的。新环境没有sys.argv
变量。它有新的标准 I/O 流文件对象sys.stdin
、sys.stdout
和sys.stderr
(但是这些引用相同的基础文件描述符)。给定的 config 控制解释器初始化的选项。
成功后,tstate_p 将设置为在新子解释器中创建的第一个线程状态。此线程状态已附加。请注意,没有实际创建线程;请参阅下面关于线程状态的讨论。如果新解释器创建失败,tstate_p 设置为
NULL
;不设置异常,因为异常状态存储在附加线程状态中,该状态可能不存在。与所有其他 Python/C API 函数一样,在调用此函数之前必须存在附加线程状态,但在返回时可能会分离。成功时,返回的线程状态将附加。如果子解释器以其自己的 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 = NULL; PyStatus status = Py_NewInterpreterFromConfig(&tstate, &config); if (PyStatus_Exception(status)) { Py_ExitStatusException(status); }
请注意,配置仅短暂使用,不会被修改。在初始化期间,配置的值会转换为各种
PyInterpreterState
值。配置的只读副本可以内部存储在PyInterpreterState
上。扩展模块在(子)解释器之间共享如下
对于使用多阶段初始化的模块,例如
PyModule_FromDefAndSpec()
,会为每个解释器创建并初始化一个单独的模块对象。只有 C 级别的静态和全局变量在这些模块对象之间共享。对于使用单阶段初始化的模块,例如
PyModule_Create()
,当某个扩展首次导入时,它会正常初始化,并将其模块字典的(浅)副本存储起来。当同一扩展被另一个(子)解释器导入时,会初始化一个新模块并用此副本的内容填充;扩展的init
函数不会被调用。因此,模块字典中的对象最终会在(子)解释器之间共享,这可能会导致意外行为(请参阅下面的错误和注意事项)。请注意,这与在通过调用
Py_FinalizeEx()
和Py_Initialize()
完全重新初始化解释器后导入扩展时发生的情况不同;在这种情况下,扩展的initmodule
函数会再次调用。与多阶段初始化一样,这意味着只有 C 级别的静态和全局变量在这些模块之间共享。
-
PyThreadState *Py_NewInterpreter(void)¶
- 作为 稳定 ABI 的一部分。
创建一个新的子解释器。这本质上只是
Py_NewInterpreterFromConfig()
的一个包装,其配置保留了现有行为。结果是一个非隔离的子解释器,它共享主解释器的 GIL,允许 fork/exec,允许守护线程,并允许单阶段 init 模块。
-
void Py_EndInterpreter(PyThreadState *tstate)¶
- 作为 稳定 ABI 的一部分。
销毁由给定线程状态表示的(子)解释器。给定的线程状态必须是附加的。当调用返回时,将没有附加线程状态。与此解释器关联的所有线程状态都将被销毁。
Py_FinalizeEx()
将销毁所有尚未明确销毁的子解释器。
每个解释器一个 GIL¶
使用 Py_NewInterpreterFromConfig()
,您可以创建一个与其他解释器完全隔离的子解释器,包括拥有自己的 GIL。这种隔离的最重要好处是,此类解释器可以执行 Python 代码,而不会被其他解释器阻塞,也不会阻塞其他任何解释器。因此,单个 Python 进程在运行 Python 代码时可以真正利用多个 CPU 核心。这种隔离还鼓励了一种不同于仅使用线程的并发方法。(参见PEP 554 和PEP 684。)
使用隔离解释器需要警惕维护这种隔离。这意味着,在没有线程安全保证的情况下,不要共享任何对象或可变状态。即使是那些通常不可变的对象(例如 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 不会被递归地中断以执行另一个异步通知,但如果线程状态分离,它仍然可以被中断以切换线程。此函数不需要附加线程状态。然而,要在子解释器中调用此函数,调用者必须具有附加线程状态。否则,函数 func 可能会被安排从错误的解释器调用。
警告
这是一个低级函数,仅适用于非常特殊的情况。无法保证 func 会尽快被调用。如果主线程忙于执行系统调用,则在系统调用返回之前不会调用 func。此函数通常不适合从任意 C 线程调用 Python 代码。相反,请使用PyGILState API。
在 3.1 版本加入。
3.9 版更改: 如果在子解释器中调用此函数,则函数 func 现在被安排从子解释器调用,而不是从主解释器调用。每个子解释器现在都有自己的预定调用列表。
3.12 版更改: 此函数现在始终将 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_CALL
、PyTrace_EXCEPTION
、PyTrace_LINE
、PyTrace_RETURN
、PyTrace_C_CALL
、PyTrace_C_EXCEPTION
、PyTrace_C_RETURN
或PyTrace_OPCODE
之一,而 arg 取决于 what 的值what 的值
arg 的含义
总是
Py_None
。由
sys.exc_info()
返回的异常信息。总是
Py_None
。返回给调用者的值,如果由异常引起则为
NULL
。正在调用的函数对象。
正在调用的函数对象。
正在调用的函数对象。
总是
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)¶
将剖析器函数设置为 func。obj 参数作为第一个参数传递给函数,可以是任何 Python 对象,或
NULL
。如果剖析函数需要维护状态,为每个线程使用不同的 obj 值提供了一个方便且线程安全的地方来存储它。剖析函数将为所有受监控事件调用,除了PyTrace_LINE
PyTrace_OPCODE
和PyTrace_EXCEPTION
。另请参阅
sys.setprofile()
函数。调用者必须具有 已附加的线程状态。
-
void PyEval_SetProfileAllThreads(Py_tracefunc func, PyObject *obj)¶
与
PyEval_SetProfile()
类似,但将剖析函数设置到属于当前解释器的所有正在运行的线程中,而不是仅设置到当前线程中。调用者必须具有 已附加的线程状态。
与
PyEval_SetProfile()
一样,此函数在设置所有线程的剖析函数时会忽略任何引发的异常。
3.12 新版功能.
-
void PyEval_SetTrace(Py_tracefunc func, PyObject *obj)¶
将跟踪函数设置为 func。这与
PyEval_SetProfile()
类似,只是跟踪函数会接收行号事件和每操作码事件,但不接收任何与 C 函数对象调用相关的事件。使用PyEval_SetTrace()
注册的任何跟踪函数都不会接收PyTrace_C_CALL
、PyTrace_C_EXCEPTION
或PyTrace_C_RETURN
作为 what 参数的值。另请参阅
sys.settrace()
函数。调用者必须具有 已附加的线程状态。
-
void PyEval_SetTraceAllThreads(Py_tracefunc func, PyObject *obj)¶
与
PyEval_SetTrace()
类似,但将跟踪函数设置到属于当前解释器的所有正在运行的线程中,而不是仅设置到当前线程中。调用者必须具有 已附加的线程状态。
与
PyEval_SetTrace()
一样,此函数在设置所有线程的跟踪函数时会忽略任何引发的异常。
3.12 新版功能.
引用跟踪¶
在 3.13 版本加入。
-
typedef int (*PyRefTracer)(PyObject*, int event, void *data)¶
使用
PyRefTracer_SetTracer()
注册的跟踪函数的类型。第一个参数是一个刚刚创建的 Python 对象(当 event 设置为PyRefTracer_CREATE
时)或即将被销毁的 Python 对象(当 event 设置为PyRefTracer_DESTROY
时)。data 参数是调用PyRefTracer_SetTracer()
时提供的不透明指针。
在 3.13 版本加入。
-
int PyRefTracer_CREATE¶
当 Python 对象被创建时,传递给
PyRefTracer
函数的 event 参数的值。
-
int PyRefTracer_DESTROY¶
当 Python 对象被销毁时,传递给
PyRefTracer
函数的 event 参数的值。
-
int PyRefTracer_SetTracer(PyRefTracer tracer, void *data)¶
注册一个引用跟踪函数。当创建新的 Python 对象或对象即将被销毁时,将调用此函数。如果提供了 data,它必须是一个不透明指针,在调用跟踪函数时将提供该指针。成功时返回
0
。出错时设置异常并返回-1
。请注意,跟踪函数不得在内部创建 Python 对象,否则调用将是重入的。跟踪器也不得清除任何现有异常或设置异常。每次调用跟踪函数时,线程状态都将处于活动状态。
调用此函数时必须存在附加线程状态。
在 3.13 版本加入。
-
PyRefTracer PyRefTracer_GetTracer(void **data)¶
获取已注册的引用跟踪函数以及在调用
PyRefTracer_SetTracer()
时注册的不透明数据指针的值。如果未注册跟踪器,此函数将返回 NULL 并将 data 指针设置为 NULL。调用此函数时必须存在附加线程状态。
在 3.13 版本加入。
高级调试器支持¶
这些函数仅供高级调试工具使用。
-
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* 值与每个线程关联。
请注意,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_tss_t
进行动态分配,这在使用 Py_LIMITED_API 构建的扩展模块中是必需的,因为在构建时其实现是不透明的,无法对该类型进行静态分配。
-
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()
初始化。此函数可以在同一个键上重复调用——在已销毁的键上调用它是一个空操作。
线程局部存储 (TLS) API¶
自 3.7 版本弃用: 此 API 已被 线程特定存储 (TSS) API 取代。
备注
此版本的 API 不支持那些本地 TLS 键定义方式无法安全地转换为 int
的平台。在此类平台上,PyThread_create_key()
将立即返回失败状态,并且其他 TLS 函数在此类平台上都将是空操作。
由于上述兼容性问题,新代码不应使用此 API 版本。
同步原语¶
C-API 提供基本的互斥锁。
-
type PyMutex¶
互斥锁。
PyMutex
应初始化为零,表示未锁定状态。例如:PyMutex mutex = {0};
PyMutex
的实例不应被复制或移动。PyMutex
的内容和地址都有意义,它必须保持在内存中固定、可写的位置。备注
PyMutex
目前占用一个字节,但其大小应被视为不稳定。未来 Python 版本中可能会更改大小,而无需弃用期。在 3.13 版本加入。
-
void PyMutex_Lock(PyMutex *m)¶
锁定互斥体 m。如果另一个线程已将其锁定,则调用线程将阻塞,直到互斥体被解锁。在阻塞期间,如果存在 线程状态,线程将暂时分离该线程状态。
在 3.13 版本加入。
-
int PyMutex_IsLocked(PyMutex *m)¶
如果互斥体 m 当前被锁定,则返回非零值,否则返回零。
备注
此函数仅用于断言和调试,不应用于做出并发控制决策,因为锁状态可能在检查后立即更改。
在 3.14 版本加入。
Python 临界区 API¶
临界区 API 在 自由线程 CPython 的对象级锁之上提供了一个死锁避免层。它们旨在取代对 全局解释器锁 的依赖,并且在具有全局解释器锁的 Python 版本中是空操作。
临界区旨在用于 C-API 扩展中实现的自定义类型。它们通常不应与 list
和 dict
等内置类型一起使用,因为它们的公共 C-API 内部已使用临界区,但 PyDict_Next()
是一个值得注意的例外,它需要外部获取临界区。
临界区通过隐式暂停活动临界区来避免死锁,因此,它们不提供传统锁(如 PyMutex
)提供的排他访问。当临界区启动时,将获取对象的对象级锁。如果在临界区内执行的代码调用 C-API 函数,则它可以暂停临界区,从而释放对象级锁,以便其他线程可以为同一对象获取对象级锁。
接受 PyMutex
指针而不是 Python 对象的变体也可用。在没有 PyObject
的情况下,使用这些变体来启动临界区——例如,当使用不扩展或包装 PyObject
但仍需要以可能导致死锁的方式调用 C API 的 C 类型时。
宏使用的函数和结构体在 C 宏不可用的情况下暴露出来。它们只能按照给定的宏展开方式使用。请注意,结构体的大小和内容可能在未来的 Python 版本中发生变化。
备注
需要同时锁定两个对象的操作必须使用 Py_BEGIN_CRITICAL_SECTION2
。你 不能 使用嵌套临界区一次锁定多个对象,因为内部临界区可能会暂停外部临界区。此 API 不提供一次锁定两个以上对象的方法。
用法示例:
static PyObject *
set_field(MyObject *self, PyObject *value)
{
Py_BEGIN_CRITICAL_SECTION(self);
Py_SETREF(self->field, Py_XNewRef(value));
Py_END_CRITICAL_SECTION();
Py_RETURN_NONE;
}
在上面的例子中,Py_SETREF
调用 Py_DECREF
,后者可以通过对象的解除分配函数调用任意代码。临界区 API 通过允许运行时在终结器触发的代码阻塞并调用 PyEval_SaveThread()
时临时暂停临界区,从而避免了由于重入和锁顺序导致的潜在死锁。
-
Py_BEGIN_CRITICAL_SECTION(op)¶
获取对象 op 的对象级锁并开始一个临界区。
在自由线程构建中,此宏展开为
{ PyCriticalSection _py_cs; PyCriticalSection_Begin(&_py_cs, (PyObject*)(op))
在默认构建中,此宏展开为
{
。在 3.13 版本加入。
-
Py_BEGIN_CRITICAL_SECTION_MUTEX(m)¶
锁定互斥锁 m 并开始一个临界区。
在自由线程构建中,此宏展开为
{ PyCriticalSection _py_cs; PyCriticalSection_BeginMutex(&_py_cs, m)
请注意,与
Py_BEGIN_CRITICAL_SECTION
不同,宏的参数没有强制转换——它必须是一个PyMutex
指针。在默认构建中,此宏展开为
{
。在 3.14 版本加入。
-
Py_END_CRITICAL_SECTION()¶
结束临界区并释放对象级锁。
在自由线程构建中,此宏展开为
PyCriticalSection_End(&_py_cs); }
在默认构建中,此宏展开为
}
。在 3.13 版本加入。
-
Py_BEGIN_CRITICAL_SECTION2(a, b)¶
获取对象 a 和 b 的对象级锁并开始一个临界区。锁以一致的顺序(最低地址优先)获取,以避免锁顺序死锁。
在自由线程构建中,此宏展开为
{ PyCriticalSection2 _py_cs2; PyCriticalSection2_Begin(&_py_cs2, (PyObject*)(a), (PyObject*)(b))
在默认构建中,此宏展开为
{
。在 3.13 版本加入。
-
Py_BEGIN_CRITICAL_SECTION2_MUTEX(m1, m2)¶
锁定互斥锁 m1 和 m2 并开始一个临界区。
在自由线程构建中,此宏展开为
{ PyCriticalSection2 _py_cs2; PyCriticalSection2_BeginMutex(&_py_cs2, m1, m2)
请注意,与
Py_BEGIN_CRITICAL_SECTION2
不同,宏的参数没有强制转换——它们必须是PyMutex
指针。在默认构建中,此宏展开为
{
。在 3.14 版本加入。
-
Py_END_CRITICAL_SECTION2()¶
结束临界区并释放对象级锁。
在自由线程构建中,此宏展开为
PyCriticalSection2_End(&_py_cs2); }
在默认构建中,此宏展开为
}
。在 3.13 版本加入。