初始化、终结和线程 — Python 文档
初始化、结束和线程
初始化和完成解释器
- void Py_Initialize()
初始化 Python 解释器。 在嵌入 Python 的应用程序中,应该在使用任何其他 Python/C API 函数之前调用它; 除了 Py_SetProgramName()、Py_SetPythonHome() 和 Py_SetPath()。 这将初始化加载模块表 (
sys.modules
),并创建基本模块 builtins、__main__ 和 sys。 它还初始化模块搜索路径 (sys.path
)。 不设置sys.argv
; 为此使用 PySys_SetArgvEx()。 这是第二次调用时的空操作(不首先调用 Py_FinalizeEx())。 没有返回值; 如果初始化失败,这是一个致命错误。笔记
在 Windows 上,将控制台模式从
O_TEXT
更改为O_BINARY
,这也会影响使用 C 运行时的控制台的非 Python 使用。
- void Py_InitializeEx(int initsigs)
- 如果 initsigs 是
1
,则此函数的工作方式类似于 Py_Initialize()。 如果 initsigs 是0
,它会跳过信号处理程序的初始化注册,这在嵌入 Python 时可能很有用。
- int Py_IsInitialized()
- 当 Python 解释器已初始化时返回真(非零),否则返回假(零)。 调用 Py_FinalizeEx() 后,返回 false,直到再次调用 Py_Initialize()。
- int Py_FinalizeEx()
撤消由 Py_Initialize() 进行的所有初始化以及 Python/C API 函数的后续使用,并销毁所有已创建但尚未销毁的子解释器(请参阅下面的 Py_NewInterpreter())自上次调用 Py_Initialize() 以来。 理想情况下,这会释放 Python 解释器分配的所有内存。 这是第二次调用时的空操作(无需先再次调用 Py_Initialize())。 通常返回值为
0
。 如果在终结过程中出现错误(刷新缓冲数据),则返回-1
。提供此功能有多种原因。 嵌入应用程序可能希望重新启动 Python,而不必重新启动应用程序本身。 从动态可加载库(或 DLL)加载 Python 解释器的应用程序可能希望在卸载 DLL 之前释放 Python 分配的所有内存。 在寻找应用程序内存泄漏的过程中,开发人员可能希望在退出应用程序之前释放 Python 分配的所有内存。
Bugs 和警告: 模块和模块中对象的销毁是按随机顺序进行的; 这可能会导致析构函数(
__del__()
方法)在依赖于其他对象(甚至函数)或模块时失败。 不会卸载 Python 加载的动态加载的扩展模块。 Python解释器分配的少量内存可能无法释放(如果发现泄漏,请报告)。 对象之间的循环引用所占用的内存不会被释放。 一些由扩展模块分配的内存可能不会被释放。 如果多次调用初始化例程,某些扩展可能无法正常工作; 如果应用程序多次调用 Py_Initialize() 和 Py_FinalizeEx(),就会发生这种情况。3.6 版中的新功能。
- void Py_Finalize()
- 这是 Py_FinalizeEx() 的向后兼容版本,忽略返回值。
工艺参数
- int Py_SetStandardStreamEncoding(const char *encoding, const char *errors)
这个函数应该在 Py_Initialize() 之前调用,如果它被调用的话。 它指定使用标准 IO 的编码和错误处理,其含义与 str.encode() 中的含义相同。
它覆盖 PYTHONIOENCODING 值,并允许嵌入代码在环境变量不起作用时控制 IO 编码。
encoding
和/或errors
可以为 NULL 以使用 PYTHONIOENCODING 和/或默认值(取决于其他设置)。请注意,sys.stderr 始终使用“backslashreplace”错误处理程序,无论此(或任何其他)设置如何。
如果Py_FinalizeEx()被调用,则需要再次调用该函数,以影响后续对Py_Initialize()的调用。
如果成功,则返回
0
,错误时返回非零值(例如 在解释器已经初始化之后调用)。3.4 版中的新功能。
- void Py_SetProgramName(wchar_t *name)
这个函数应该在第一次调用 Py_Initialize() 之前调用,如果它被调用的话。 它告诉解释器程序的
main()
函数的argv[0]
参数的值(转换为宽字符)。 Py_GetPath() 和下面的一些其他函数使用它来查找与解释器可执行文件相关的 Python 运行时库。 默认值为'python'
。 该参数应指向静态存储中以零结尾的宽字符串,其内容在程序执行期间不会更改。 Python 解释器中的任何代码都不会更改此存储的内容。使用 Py_DecodeLocale() 解码字节字符串以获得
wchar_*
字符串。
- wchar *Py_GetProgramName()
- 返回用 Py_SetProgramName() 设置的程序名称,或默认值。 返回的字符串指向静态存储; 调用者不应修改其值。
- wchar_t *Py_GetPrefix()
- 为已安装的独立于平台的文件返回 前缀 。 这是通过一些复杂的规则从用 Py_SetProgramName() 和一些环境变量设置的程序名称得出的; 例如,如果程序名称为
'/usr/local/bin/python'
,则前缀为'/usr/local'
。 返回的字符串指向静态存储; 调用者不应修改其值。 这对应于顶层Makefile
中的 prefix 变量和构建时 configure 脚本的--prefix
参数。 该值可用于 Python 代码作为sys.prefix
。 它仅在 Unix 上有用。 另请参阅下一个函数。
- wchar_t *Py_GetExecPrefix()
为已安装的平台 相关 文件返回 exec-prefix。 这是通过一些复杂的规则从用 Py_SetProgramName() 和一些环境变量设置的程序名称得出的; 例如,如果程序名称为
'/usr/local/bin/python'
,则exec-prefix 为'/usr/local'
。 返回的字符串指向静态存储; 调用者不应修改其值。 这对应于顶层Makefile
中的 exec_prefix 变量和构建时 configure 脚本的--exec-prefix
参数。 该值可用于 Python 代码作为sys.exec_prefix
。 它仅在 Unix 上有用。背景:当平台相关文件(例如可执行文件和共享库)安装在不同的目录树中时,exec-prefix 与前缀不同。 在典型安装中,平台相关文件可能安装在
/usr/local/plat
子树中,而平台无关文件可能安装在/usr/local
中。一般来说,平台是硬件和软件家族的组合,例如 运行 Solaris 2.x 操作系统的 Sparc 机器被认为是同一平台,但运行 Solaris 2.x 的 Intel 机器是另一个平台,而运行 Linux 的 Intel 机器又是另一个平台。 同一操作系统的不同大版本通常也形成不同的平台。 非 Unix 操作系统则是另一回事。 这些系统上的安装策略是如此不同,以至于前缀和 exec-prefix 没有意义,并设置为空字符串。 请注意,编译后的 Python 字节码文件是平台无关的(但不独立于编译它们的 Python 版本!)。
系统管理员将知道如何配置 mount 或 automount 程序以在平台之间共享
/usr/local
,同时让/usr/local/plat
成为每个平台的不同文件系统。
- wchar_t *Py_GetProgramFullPath()
- 返回 Python 可执行文件的完整程序名称; 这被计算为从程序名称(由上面的 Py_SetProgramName() 设置)派生默认模块搜索路径的副作用。 返回的字符串指向静态存储; 调用者不应修改其值。 该值可用于 Python 代码作为
sys.executable
。
- wchar_t *Py_GetPath()
- 返回默认模块搜索路径; 这是根据程序名称(由上面的 Py_SetProgramName() 设置)和一些环境变量计算得出的。 返回的字符串由一系列由平台相关的定界符分隔的目录名称组成。 分隔符在 Unix 和 Mac OS X 上是
':'
,在 Windows 上是';'
。 返回的字符串指向静态存储; 调用者不应修改其值。 列表 sys.path 在解释器启动时用这个值初始化; 它可以(通常是)稍后修改以更改加载模块的搜索路径。
- void Py_SetPath(const wchar_t*)
设置默认模块搜索路径。 如果在 Py_Initialize() 之前调用此函数,则 Py_GetPath() 不会尝试计算默认搜索路径,而是使用提供的搜索路径。 如果 Python 是由完全了解所有模块位置的应用程序嵌入的,这将非常有用。 路径组件应由平台相关的定界符分隔,在 Unix 和 Mac OS X 上为
':'
,在 Windows 上为';'
。这也会导致 sys.executable 仅设置为原始程序名称(请参阅 Py_SetProgramName())以及 sys.prefix 和 sys。 exec_prefix 为空。 如果需要,调用者可以在调用 Py_Initialize() 后修改这些。
使用 Py_DecodeLocale() 解码字节字符串以获得
wchar_*
字符串。路径参数在内部复制,因此调用者可以在调用完成后释放它。
- const char *Py_GetVersion()
返回此 Python 解释器的版本。 这是一个看起来像的字符串
"3.0a5+ (py3k:63103M, May 12 2008, 00:53:55) \n[GCC 4.2.3]"
第一个单词(直到第一个空格字符)是当前的 Python 版本; 前三个字符是用句点分隔的主要和次要版本。 返回的字符串指向静态存储; 调用者不应修改其值。 该值可用于 Python 代码作为 sys.version。
- const char *Py_GetPlatform()
- 返回当前平台的平台标识符。 在 Unix 上,这由操作系统的“官方”名称组成,转换为小写,后跟主要修订号; 例如,对于 Solaris 2.x,也称为 SunOS 5.x,该值为
'sunos5'
。 在 Mac OS X 上,它是'darwin'
。 在 Windows 上,它是'win'
。 返回的字符串指向静态存储; 调用者不应修改其值。 该值可用于 Python 代码作为sys.platform
。
- const char *Py_GetCopyright()
返回当前 Python 版本的官方版权字符串,例如
'Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam'
返回的字符串指向静态存储; 调用者不应修改其值。 该值可用于 Python 代码作为
sys.copyright
。
- const char *Py_GetCompiler()
在方括号中返回用于构建当前 Python 版本的编译器的指示,例如:
"[GCC 2.7.2.2]"
返回的字符串指向静态存储; 调用者不应修改其值。 该值作为变量
sys.version
的一部分可用于 Python 代码。
- const char *Py_GetBuildInfo()
返回有关当前 Python 解释器实例的序列号和构建日期和时间的信息,例如
"#67, Aug 1 1997, 22:34:28"
返回的字符串指向静态存储; 调用者不应修改其值。 该值作为变量
sys.version
的一部分可用于 Python 代码。
- void PySys_SetArgvEx(int argc, wchar_t **argv, int updatepath)
根据 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_*
字符串。笔记
建议应用程序嵌入 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 中的新功能。
- void PySys_SetArgv(int argc, wchar_t **argv)
此函数的工作方式类似于 PySys_SetArgvEx(),其中 updatepath 设置为
1
,除非 python 解释器是用 -I 启动的]。使用 Py_DecodeLocale() 解码字节字符串以获得
wchar_*
字符串。3.4 版更改: updatepath 值取决于 -I。
- void Py_SetPythonHome(wchar_t *home)
设置默认的“home”目录,即标准 Python 库的位置。 有关参数字符串的含义,请参阅 PYTHONHOME。
该参数应指向静态存储中以零结尾的字符串,其内容在程序执行期间不会更改。 Python 解释器中的任何代码都不会更改此存储的内容。
使用 Py_DecodeLocale() 解码字节字符串以获得
wchar_*
字符串。
- w_char *Py_GetPythonHome()
- 返回默认的“home”,即之前调用 Py_SetPythonHome() 设置的值,或者 PYTHONHOME 环境变量的值,如果它设置。
线程状态和全局解释器锁
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 宏关闭块。 在没有线程支持的情况下编译 Python 时,这两个宏仍然可用(它们只是有一个空扩展)。
当启用线程支持时,上面的块扩展为以下代码:
PyThreadState *_save;
_save = PyEval_SaveThread();
...Do some blocking I/O operation...
PyEval_RestoreThread(_save);
以下是这些函数的工作原理:全局解释器锁用于保护指向当前线程状态的指针。 在释放锁和保存线程状态时,必须在释放锁之前检索当前线程状态指针(因为另一个线程可以立即获取锁并将自己的线程状态存储在全局变量中)。 反之,在获取锁和恢复线程状态时,必须在存储线程状态指针之前获取锁。
非 Python 创建的线程
当使用专用 Python API(例如 threading 模块)创建线程时,线程状态会自动关联到它们,因此上面显示的代码是正确的。 但是,当线程是从 C 创建的(例如由具有自己线程管理的第三方库)时,它们不持有 GIL,也没有线程状态结构。
如果您需要从这些线程调用 Python 代码(通常这将是上述第三方库提供的回调 API 的一部分),您必须首先通过创建线程状态数据结构向解释器注册这些线程,然后获取GIL,最后存储它们的线程状态指针,然后才能开始使用 Python/C API。 完成后,应该重置线程状态指针,释放 GIL,最后释放线程状态数据结构。
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。
关于线程的另一个需要注意的重要事项是它们在面对 C fork()
调用时的行为。 在大多数具有 fork()
的系统上,在进程 fork 之后,只有发出 fork 的线程才会存在。 这也意味着其他线程持有的任何锁永远不会被释放。 Python 为 os.fork() 解决了这个问题,方法是在 fork 之前获取它内部使用的锁,然后释放它们。 此外,它会重置子进程中的任何 锁定对象 。 在扩展或嵌入 Python 时,无法通知 Python 需要在 fork 之前获取或在 fork 之后重置的其他(非 Python)锁。 需要使用诸如 pthread_atfork()
之类的操作系统工具来完成相同的事情。 此外,在扩展或嵌入 Python 时,直接调用 fork()
而不是通过 os.fork()(并返回或调用 Python)可能会导致 Python 内部锁之一的死锁由分叉后失效的线程持有。 PyOS_AfterFork() 尝试重置必要的锁,但并不总是能够。
高级 API
这些是编写 C 扩展代码或嵌入 Python 解释器时最常用的类型和函数:
- type PyInterpreterState
该数据结构表示多个协作线程共享的状态。 属于同一个解释器的线程共享它们的模块管理和一些其他内部项目。 此结构中没有公共成员。
属于不同解释器的线程最初不共享任何东西,除了可用内存、打开的文件描述符等进程状态之外。 全局解释器锁也由所有线程共享,无论它们属于哪个解释器。
- type PyThreadState
- 该数据结构表示单个线程的状态。 唯一的公共数据成员是
PyInterpreterState *
interp
,它指向这个线程的解释器状态。
- void PyEval_InitThreads()
初始化并获取全局解释器锁。 在创建第二个线程或进行任何其他线程操作(例如
PyEval_ReleaseThread(tstate)
)之前,应在主线程中调用它。 在调用 PyEval_SaveThread() 或 PyEval_RestoreThread() 之前不需要它。这是第二次调用时的空操作。
3.2 版本更改: 该函数不能在 Py_Initialize() 之前调用了。
笔记
当只有主线程存在时,不需要 GIL 操作。 这是一种常见情况(大多数 Python 程序不使用线程),并且锁定操作会稍微减慢解释器的速度。 因此,最初不会创建锁。 这种情况相当于获得了锁:当只有一个线程时,所有的对象访问都是安全的。 因此,当这个函数初始化全局解释器锁时,它也会获取它。 在 Python _thread 模块创建新线程之前,知道它有锁或尚未创建锁,它调用 PyEval_InitThreads()。 当此调用返回时,可以保证已创建锁并且调用线程已获取它。
当未知哪个线程(如果有)当前拥有全局解释器锁时,调用此函数是 不是 安全的。
当在编译时禁用线程支持时,此函数不可用。
- int PyEval_ThreadsInitialized()
- 如果 PyEval_InitThreads() 已被调用,则返回一个非零值。 可以在不持有 GIL 的情况下调用此函数,因此可用于避免在运行单线程时调用锁定 API。 当在编译时禁用线程支持时,此函数不可用。
- PyThreadState *PyEval_SaveThread()
- 释放全局解释器锁(如果已创建并启用线程支持)并将线程状态重置为 NULL,返回之前的线程状态(不是 NULL)。 如果已创建锁,则当前线程必须已获取它。 (即使在编译时禁用线程支持,此函数也可用。)
- void PyEval_RestoreThread(PyThreadState *tstate)
- 获取全局解释器锁(如果已经创建并开启线程支持),设置线程状态为tstate,不能为NULL。 如果已创建锁,则当前线程一定没有获取它,否则会发生死锁。 (即使在编译时禁用线程支持,此函数也可用。)
- PyThreadState *PyThreadState_Get()
- 返回当前线程状态。 必须持有全局解释器锁。 当前线程状态为 NULL 时,会发出致命错误(因此调用者无需检查 NULL)。
- PyThreadState *PyThreadState_Swap(PyThreadState *tstate)
- 将当前线程状态与参数 tstate 给出的线程状态交换,可能是 NULL。 全局解释器锁必须被持有并且不会被释放。
- void PyEval_ReInitThreads()
- 该函数是从 PyOS_AfterFork() 调用的,以确保新创建的子进程不会持有指向未在子进程中运行的线程的锁。
以下函数使用线程本地存储,并且与子解释器不兼容:
- PyGILState_STATE PyGILState_Ensure()
确保当前线程准备好调用 Python C API,而不管 Python 或全局解释器锁的当前状态如何。 只要每个调用都与对 PyGILState_Release() 的调用相匹配,就可以根据需要多次调用它。 一般来说,其他线程相关的 API 可以在 PyGILState_Ensure() 和 PyGILState_Release() 调用之间使用,只要线程状态恢复到 Release() 之前的先前状态即可。 例如,正常使用 Py_BEGIN_ALLOW_THREADS 和 Py_END_ALLOW_THREADS 宏是可以接受的。
返回值是调用 PyGILState_Ensure() 时线程状态的不透明“句柄”,并且必须传递给 PyGILState_Release() 以确保 Python 处于相同状态。 即使允许递归调用,这些句柄 也不能 共享 - 对 PyGILState_Ensure() 的每个唯一调用都必须保存对 PyGILState_Release() 的调用的句柄。
当函数返回时,当前线程将持有 GIL 并能够调用任意 Python 代码。 失败是致命的错误。
- void PyGILState_Release(PyGILState_STATE)
释放之前获得的任何资源。 在此调用之后,Python 的状态将与相应的 PyGILState_Ensure() 调用之前的状态相同(但通常调用者不知道此状态,因此使用 GILState API)。
对 PyGILState_Ensure() 的每次调用都必须与在同一线程上对 PyGILState_Release() 的调用相匹配。
- PyThreadState *PyGILState_GetThisThreadState()
- 获取此线程的当前线程状态。 如果当前线程上没有使用 GILState API,则可能返回
NULL
。 请注意,主线程始终具有这样的线程状态,即使没有在主线程上进行自动线程状态调用。 这主要是一个辅助/诊断功能。
- int PyGILState_Check()
如果当前线程持有 GIL,则返回
1
,否则返回0
。 可以随时从任何线程调用此函数。 只有当它初始化了 Python 线程状态并且当前持有 GIL 时,它才会返回1
。 这主要是一个辅助/诊断功能。 例如在回调上下文或内存分配函数中,当知道 GIL 被锁定可以允许调用者执行敏感操作或以其他方式表现不同时,它可能很有用。3.4 版中的新功能。
以下宏通常不带尾随分号使用; 在 Python 源代码分发中查找示例用法。
- Py_BEGIN_ALLOW_THREADS
- 该宏扩展为
{ PyThreadState *_save; _save = PyEval_SaveThread();
。 请注意,它包含一个左括号; 它必须与以下 Py_END_ALLOW_THREADS 宏匹配。 有关此宏的进一步讨论,请参见上文。 当在编译时禁用线程支持时,这是一个空操作。
- Py_END_ALLOW_THREADS
- 该宏扩展为
PyEval_RestoreThread(_save); }
。 请注意,它包含一个右括号; 它必须与较早的 Py_BEGIN_ALLOW_THREADS 宏匹配。 有关此宏的进一步讨论,请参见上文。 当在编译时禁用线程支持时,这是一个空操作。
- Py_BLOCK_THREADS
- 这个宏扩展为
PyEval_RestoreThread(_save);
:它相当于没有右大括号的 Py_END_ALLOW_THREADS。 当在编译时禁用线程支持时,这是一个空操作。
- Py_UNBLOCK_THREADS
- 这个宏扩展为
_save = PyEval_SaveThread();
:它相当于 Py_BEGIN_ALLOW_THREADS 没有左大括号和变量声明。 当在编译时禁用线程支持时,这是一个空操作。
低级 API
以下所有函数仅在编译时启用线程支持时才可用,并且只有在创建全局解释器锁时才必须调用。
- PyInterpreterState *PyInterpreterState_New()
- 创建一个新的解释器状态对象。 不需要持有全局解释器锁,但如果需要序列化对此函数的调用,则可以持有。
- void PyInterpreterState_Clear(PyInterpreterState *interp)
- 重置解释器状态对象中的所有信息。 必须持有全局解释器锁。
- void PyInterpreterState_Delete(PyInterpreterState *interp)
- 销毁解释器状态对象。 不需要持有全局解释器锁。 解释器状态必须在先前调用 PyInterpreterState_Clear() 时重置。
- PyThreadState *PyThreadState_New(PyInterpreterState *interp)
- 创建一个属于给定解释器对象的新线程状态对象。 不需要持有全局解释器锁,但如果需要序列化对此函数的调用,则可以持有。
- void PyThreadState_Clear(PyThreadState *tstate)
- 重置线程状态对象中的所有信息。 必须持有全局解释器锁。
- void PyThreadState_Delete(PyThreadState *tstate)
- 销毁线程状态对象。 不需要持有全局解释器锁。 线程状态必须在先前调用 PyThreadState_Clear() 时重置。
- PyObject *PyThreadState_GetDict()
- 返回一个字典,扩展可以在其中存储线程特定的状态信息。 每个扩展都应该使用一个唯一的键来存储字典中的状态。 当没有可用的当前线程状态时调用此函数是可以的。 如果此函数返回 NULL,则未引发异常,调用者应假定当前没有可用的线程状态。
- int PyThreadState_SetAsyncExc(long id, PyObject *exc)
- 在线程中异步引发异常。 id 参数是目标线程的线程 id; exc 是要引发的异常对象。 此函数不会窃取对 exc 的任何引用。 为了防止天真的误用,您必须编写自己的 C 扩展来调用它。 必须在持有 GIL 的情况下调用。 返回修改的线程状态数; 这通常为 1,但如果未找到线程 ID,则该值为 0。 如果 exc 是
NULL
,则清除线程的未决异常(如果有)。 这不会引发任何例外。
- void PyEval_AcquireThread(PyThreadState *tstate)
获取全局解释器锁,设置当前线程状态为tstate,不应为NULL。 锁一定是更早创建的。 如果这个线程已经拥有锁,就会发生死锁。
PyEval_RestoreThread() 是一个始终可用的高级函数(即使未启用线程支持或未初始化线程)。
- void PyEval_ReleaseThread(PyThreadState *tstate)
将当前线程状态重置为 NULL 并释放全局解释器锁。 锁必须早先创建并且必须由当前线程持有。 tstate 参数不能是 NULL,仅用于检查它是否代表当前线程状态——如果不是,则报告一个致命错误。
PyEval_SaveThread() 是一个始终可用的高级函数(即使未启用线程支持或未初始化线程)。
- void PyEval_AcquireLock()
获取全局解释器锁。 锁一定是更早创建的。 如果这个线程已经拥有锁,就会发生死锁。
自 3.2 版起已弃用: 此函数不更新当前线程状态。 请改用 PyEval_RestoreThread() 或 PyEval_AcquireThread()。
- void PyEval_ReleaseLock()
释放全局解释器锁。 锁一定是更早创建的。
自 3.2 版起已弃用: 此函数不更新当前线程状态。 请改用 PyEval_SaveThread() 或 PyEval_ReleaseThread()。
副译员支持
虽然在大多数用途中,您只会嵌入一个 Python 解释器,但在某些情况下,您需要在同一进程中甚至在同一线程中创建多个独立的解释器。 副口译员允许您这样做。 您可以使用 PyThreadState_Swap() 函数在子解释器之间切换。 您可以使用以下函数创建和销毁它们:
- PyThreadState *Py_NewInterpreter()
创建一个新的子解释器。 这是一个(几乎)完全独立的 Python 代码执行环境。 特别是,新的解释器具有所有导入模块的独立版本,包括基本模块 builtins、__main__ 和 sys。 加载模块表(
sys.modules
)和模块搜索路径(sys.path
)也是分开的。 新环境没有sys.argv
变量。 它具有新的标准 I/O 流文件对象sys.stdin
、sys.stdout
和sys.stderr
(但是这些是指相同的底层文件描述符)。返回值指向在新子解释器中创建的第一个线程状态。 该线程状态是在当前线程状态下进行的。 请注意,没有创建实际线程; 请参阅下面对线程状态的讨论。 如果创建新解释器不成功,则返回 NULL; 没有设置异常,因为异常状态存储在当前线程状态中,并且可能没有当前线程状态。 (与所有其他 Python/C API 函数一样,全局解释器锁必须在调用此函数之前保持,并在返回时仍保持;但是,与大多数其他 Python/C API 函数不同的是,在入口。)
扩展模块在(子)解释器之间共享如下:第一次导入特定扩展时,它被正常初始化,并且其模块字典的(浅)副本被松鼠。 当另一个(子)解释器导入相同的扩展时,会初始化一个新模块并填充此副本的内容; 扩展的
init
函数未被调用。 请注意,这与通过调用 Py_FinalizeEx() 和 Py_Initialize() 完全重新初始化解释器后导入扩展时发生的情况不同; 在这种情况下,扩展的initmodule
函数 is 再次被调用。
- void Py_EndInterpreter(PyThreadState *tstate)
- 销毁由给定线程状态表示的(子)解释器。 给定的线程状态必须是当前线程状态。 请参阅下面对线程状态的讨论。 当调用返回时,当前线程状态为 NULL。 与此解释器关联的所有线程状态都将被销毁。 (全局解释器锁必须在调用这个函数之前被持有,并且在它返回时仍然被持有。) Py_FinalizeEx() 将销毁所有当时没有被明确销毁的子解释器。
错误和警告
因为子解释器(和主解释器)是同一个进程的一部分,它们之间的隔离并不完美——例如,使用像 os.close() 这样的低级文件操作,它们可以(意外或恶意)影响彼此的打开文件。 由于(子)解释器之间共享扩展的方式,某些扩展可能无法正常工作; 当扩展使用(静态)全局变量时,或者扩展在初始化后操作其模块的字典时,这尤其可能。 可以将在一个子解释器中创建的对象插入到另一个子解释器的命名空间中; 这应该非常小心,以避免在子解释器之间共享用户定义的函数、方法、实例或类,因为由这些对象执行的导入操作可能会影响错误的(子)解释器加载模块的字典。
另请注意,将此功能与 PyGILState_*()
API 相结合是很微妙的,因为这些 API 假设 Python 线程状态和操作系统级线程之间存在双射,而子解释器的存在打破了这一假设。 强烈建议您不要在一对匹配的 PyGILState_Ensure() 和 PyGILState_Release() 调用之间切换子解释器。 此外,使用这些 API 以允许从非 Python 创建的线程调用 Python 代码的扩展(例如 ctypes)在使用子解释器时可能会被破坏。
异步通知
提供了一种机制来向主解释器线程发出异步通知。 这些通知采用函数指针和空指针参数的形式。
- int Py_AddPendingCall(int (*func)(void*), void *arg)
调度从主解释器线程调用的函数。 成功时,返回
0
并且 func 排队等待在主线程中调用。 失败时,返回-1
而不设置任何异常。当成功排队时,func 将被 最终 从主解释器线程调用,参数为 arg。 它将相对于正常运行的 Python 代码异步调用,但满足以下两个条件:
func 必须在成功时返回
0
,或者在失败时返回-1
并设置异常。 func不会被中断递归执行另一个异步通知,但如果全局解释器锁被释放,仍然可以被中断切换线程。这个函数不需要当前线程状态来运行,也不需要全局解释器锁。
警告
这是一个低级函数,只对非常特殊的情况有用。 不能保证 func 会尽快被调用。 如果主线程正忙于执行系统调用,则在系统调用返回之前不会调用 func。 这个函数一般而不是适合从任意C线程调用Python代码。 相反,使用 PyGILState API。
3.1 版中的新功能。
分析和跟踪
Python 解释器为附加分析和执行跟踪工具提供了一些低级支持。 这些用于分析、调试和覆盖率分析工具。
此 C 接口允许分析或跟踪代码避免通过 Python 级可调用对象进行调用的开销,而是进行直接 C 函数调用。 设施的基本属性没有改变; 该接口允许为每个线程安装跟踪函数,并且报告给跟踪函数的基本事件与之前版本中报告给 Python 级跟踪函数的基本事件相同。
- typedef int (*Py_tracefunc)(PyObject *obj, PyFrameObject *frame, int what, PyObject *arg)
使用 PyEval_SetProfile() 和 PyEval_SetTrace() 注册的跟踪函数的类型。 第一个参数是传递给注册函数的对象obj,frame是事件所属的帧对象,what是其中一个常量[ X189X]、
PyTrace_EXCEPTION
、PyTrace_LINE
、PyTrace_RETURN
、PyTrace_C_CALL
、PyTrace_C_EXCEPTION
或 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
被调用的函数对象。
- int PyTrace_CALL
- Py_tracefunc 函数的 what 参数值,当报告对函数或方法的新调用,或生成器的新条目时。 请注意,不会报告为生成器函数创建迭代器,因为没有控制转移到相应帧中的 Python 字节码。
- int PyTrace_EXCEPTION
- 引发异常时 Py_tracefunc 函数的 what 参数的值。 在处理任何字节码之后,在正在执行的帧内设置异常后,将使用 what 的此值调用回调函数。 这样做的效果是,当异常传播导致 Python 堆栈展开时,回调会在异常传播时返回到每一帧时调用。 只有跟踪函数接收这些事件; 分析器不需要它们。
- int PyTrace_LINE
- 当报告行号事件时,作为 what 参数传递给跟踪函数(但不是分析函数)的值。
- 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 参数的值。
- void PyEval_SetProfile(Py_tracefunc func, PyObject *obj)
- 将探查器功能设置为 func。 obj 参数作为第一个参数传递给函数,可以是任何 Python 对象,或 NULL。 如果 profile 函数需要维护状态,则为每个线程使用不同的 obj 值提供了一个方便且线程安全的地方来存储它。 对除
PyTrace_LINE
和PyTrace_EXCEPTION
之外的所有监视事件调用配置文件函数。
- void PyEval_SetTrace(Py_tracefunc func, PyObject *obj)
- 将跟踪功能设置为func。 这类似于 PyEval_SetProfile(),除了跟踪函数确实接收行号事件并且不接收与被调用的 C 函数对象相关的任何事件。 任何使用 PyEval_SetTrace() 注册的跟踪函数都不会接收
PyTrace_C_CALL
、PyTrace_C_EXCEPTION
或PyTrace_C_RETURN
作为 what 参数的值.
- PyObject *PyEval_GetCallStats(PyObject *self)
返回函数调用计数的元组。 为元组中的位置定义了常量:
名称
价值
PCALL_ALL
0
PCALL_FUNCTION
1
PCALL_FAST_FUNCTION
2
PCALL_FASTER_FUNCTION
3
PCALL_METHOD
4
PCALL_BOUND_METHOD
5
PCALL_CFUNCTION
6
PCALL_TYPE
7
PCALL_GENERATOR
8
PCALL_OTHER
9
PCALL_POP
10
PCALL_FAST_FUNCTION
表示不需要创建参数元组。PCALL_FASTER_FUNCTION
表示使用快速路径帧设置代码。如果存在可以通过更改参数元组并直接调用函数来优化调用的方法调用,则它会被记录两次。
此函数仅在 Python 编译时定义了
CALL_PROFILE
时才存在。
高级调试器支持
这些函数仅供高级调试工具使用。
- PyInterpreterState *PyInterpreterState_Head()
- 返回位于所有此类对象列表开头的解释器状态对象。
- PyInterpreterState *PyInterpreterState_Next(PyInterpreterState *interp)
- 从所有此类对象的列表中返回 interp 之后的下一个解释器状态对象。
- PyThreadState *PyInterpreterState_ThreadHead(PyInterpreterState *interp)
- 返回指向与解释器 interp 关联的线程列表中第一个 PyThreadState 对象的指针。
- PyThreadState *PyThreadState_Next(PyThreadState *tstate)
- 从属于同一 PyInterpreterState 对象的所有此类对象的列表中返回 tstate 之后的下一个线程状态对象。