初始化,完成和线程

初始化和完成解释器

2.4 版的新Function。

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

错误和警告: 破坏模块和模块中的对象的 Sequences 是随机的;当析构函数(del()方法)依赖于其他对象(甚至函数)或模块时,它们可能会失败。不会卸载由 Python 加载的动态加载的扩展模块。 Python 解释器分配的少量内存可能无法释放(如果发现泄漏,请报告)。不能释放对象之间循环引用中占用的内存。扩展模块分配的某些内存可能无法释放。如果某些扩展的初始化例程被多次调用,则它们可能无法正常工作。如果应用程序多次调用Py_Initialize()Py_Finalize(),则会发生这种情况。

Process-wide parameters

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

一般来说,平台是硬件和软件系列的组合,例如运行 Solaris 2.xos 的 Sparc 计算机被视为同一平台,但是运行 Solaris 2.x 的 Intel 计算机是另一个平台,运行 Linux 的 Intel 计算机又是另一个平台。同一 os 的不同主要修订版通常还形成不同的平台。非 Unixos 则不同。这些系统上的安装策略非常不同,因此前缀和 exec-prefix 毫无意义,并设置为空字符串。请注意,已编译的 Python 字节码文件与平台无关(但与编译它们的 Python 版本无关)。

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

"1.5 (#67, Dec 31 1997, 22:34:28) [GCC 2.7.2.2]"

第一个单词(直到第一个空格字符)是当前的 Python 版本;前三个字符是主要和次要版本,以句点分隔。返回的字符串指向静态存储;调用方不应修改其值。该值可用于sys.version的 Python 代码。

'Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam'

返回的字符串指向静态存储;调用方不应修改其值。该值可用于sys.copyright的 Python 代码。

"[GCC 2.7.2.2]"

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

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

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

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

Note

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

在 2.6.6 之前的版本中,可以pass调用PySys_SetArgv()后手动弹出第一个sys.path元素来达到相同的效果,例如使用:

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

2.6.6 版中的新Function。

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

线程状态和全局解释器锁定

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

因此,存在这样的规则,即只有获得GIL的线程才可以在 Python 对象上操作或调用 Python/C API 函数。为了模拟执行的并发性,解释器会定期try切换线程(请参见sys.setcheckinterval())。该锁也释放在可能阻止 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.

这很常见,因此可以使用Pair宏来简化它:

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);

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

Note

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

非 Python 创建的线程

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

如果您需要从这些线程中调用 Python 代码(通常这将是上述第三方库提供的回调 API 的一部分),则必须首先pass创建线程状态数据结构向解释器注册这些线程,然后获取 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()的系统上,在进程进行分叉之后,仅存在发出该分叉的线程。这也意味着其他线程持有的所有锁将永远不会释放。 Python pass在派生之前获取其内部使用的锁并在之后释放它们来解决os.fork()的问题。此外,它将重置子对象中的任何Lock Objects。在扩展或嵌入 Python 时,无法将分叉之前需要获取的其他(非 Python)锁通知 Python。需要使用诸如pthread_atfork()之类的 OS 设施来完成同一件事。另外,在扩展或嵌入 Python 时,直接调用fork()而不是passos.fork()(然后返回或调用 Python)可能会导致死锁,因为一个内部线程之一持有 Python 的内部锁,而该线程在分叉后已失效。 PyOS_AfterFork()try重置必要的锁,但并非总是能够。

High-level API

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

属于不同解释器的线程最初不共享任何内容,除了进程状态(如可用内存,打开的文件 Descriptors 等)外。全局解释器锁也由所有线程共享,无论它们属于哪个解释器。

第二次调用时,此操作不可操作。在调用Py_Initialize()之前可以安全地调用此函数。

Note

当仅存在主线程时,不需要 GIL 操作。这是一种常见情况(大多数 Python 程序不使用线程),并且锁定操作会使解释器变慢。因此,锁不是最初创建的。这种情况等同于获得锁:只有一个线程时,所有对象访问都是安全的。因此,当此函数初始化全局解释器锁时,它也会获取它。在 Python _thread模块创建新线程之前,知道它具有锁或尚未创建锁,它会调用PyEval_InitThreads()。当此调用返回时,可以确保已创建锁,并且调用线程已获取该锁。

当未知哪个线程(如果有)当前具有全局解释器锁时,调用此函数是不安全的。

在编译时禁用线程支持时,此Function不可用。

2.4 版的新Function。

以下函数使用线程本地存储,并且与子解释器不兼容:

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

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

2.3 版的新Function。

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

2.3 版的新Function。

2.3 版的新Function。

以下宏通常不带尾部分号使用;在 Python 源代码发布中查找示例用法。

Low-level API

以下所有Function仅在编译时启用线程支持时可用,并且仅在创建全局解释器锁时才必须调用。

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

在版本 2.3 中进行了更改:以前只能在当前线程处于活动状态时调用此函数,并且* NULL *表示引发了异常。

2.3 版的新Function。

PyEval_RestoreThread()是始终可用的更高级别的函数(即使未启用线程支持或尚未初始化线程时)。

PyEval_SaveThread()是始终可用的更高级别的函数(即使未启用线程支持或尚未初始化线程时)。

Warning

此函数不会更改当前线程状态。请改用PyEval_RestoreThread()PyEval_AcquireThread()

Warning

此函数不会更改当前线程状态。请改用PyEval_SaveThread()PyEval_ReleaseThread()

Sub-interpreter support

尽管在大多数使用中,您只会嵌入一个 Python 解释器,但在某些情况下,您需要在同一过程中甚至在同一线程中创建多个独立的解释器。子解释器允许您执行此操作。您可以使用PyThreadState_Swap()Function在子解释器之间切换。您可以使用以下Function创建和销毁它们:

返回值指向在新的子解释器中创建的第一个线程状态。该线程状态是在当前线程状态下进行的。请注意,没有创建任何实际的线程。请参阅下面的线程状态讨论。如果创建新解释器不成功,则返回* NULL *;否则,返回 NULL。未设置任何异常,因为异常状态存储在当前线程状态中,并且可能没有当前线程状态。 (与所有其他 Python/C API 函数一样,全局解释器锁必须在调用此函数之前保留,并在返回时仍保留;但是,与大多数其他 Python/C API 函数不同,不需要在其上具有当前线程状态条目.)

扩展模块在(子)解释器之间共享,如下所示:第一次导入特定 extensions 时,通常会对其进行初始化,然后将其模块字典的(浅)副本压缩掉。当另一个(子)解释程序导入相同的 extensions 时,将初始化一个新模块,并用此副本的内容填充该模块;扩展的init函数未调用。请注意,这与在pass调用Py_Finalize()Py_Initialize()完全重新初始化解释器后导入 extensions 时的情况不同。在这种情况下,扩展的initmodule函数将被再次调用。

错误和警告

因为子解释器(和主解释器)是同一过程的一部分,所以它们之间的隔离并不是完美的-例如,使用诸如os.close()之类的低级文件操作,它们可以(偶然或恶意地)影响彼此的打开文件。由于扩展是在(子)解释程序之间共享的方式,因此某些扩展可能无法正常工作。当扩展程序使用(静态)全局变量时,或者扩展程序在初始化后对其模块的字典进行操作时,这尤其可能发生。可以将在一个子解释器中创建的对象插入到另一个子解释器的名称空间中。这样做应格外小心,以避免在子解释器之间共享用户定义的函数,方法,实例或类,因为此类对象执行的导入操作可能会影响错误的(子)解释器的已加载模块字典。

另请注意,将此Function与PyGILState_*() API 结合使用非常困难,因为这些 API 假定 Python 线程状态与 OS 级线程之间存在双射,这一假设因存在子解释器而被 break。强烈建议您不要在Pair匹配的PyGILState_Ensure()PyGILState_Release()调用之间切换子解释器。此外,使用子解释器时,使用这些 API 允许从非 Python 创建的线程调用 Python 代码的扩展(例如ctypes)可能会被破坏。

Asynchronous Notifications

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

成功排队后,* func finally将从主解释器线程中以参数* arg *调用。对于正常运行的 Python 代码,它将被异步调用,但同时满足以下两个条件:

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

Warning

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

2.7 版的新Function。

分析和跟踪

Python 解释器为附加概要分析和执行跟踪Function提供了一些底层支持。这些用于概要分析,调试和覆盖率分析工具。

从 Python 2.2 开始,对该工具的实现进行了重大修改,并添加了 C 语言的接口。此 C 接口允许概要分析或跟踪代码避免pass Python 级可调用对象进行调用的开销,而直接进行 C 函数调用。设施的基本属性没有改变;该接口允许按线程安装跟踪Function,并且报告给该跟踪Function的基本事件与先前版本中报告给 Python 级跟踪Function的事件相同。

什么的价值 * arg *的含义
PyTrace_CALL Always Py_None.
PyTrace_EXCEPTION sys.exc_info()返回的异常信息。
PyTrace_LINE Always Py_None.
PyTrace_RETURN 返回给调用方的值,如果是由异常引起的,则为* NULL *。
PyTrace_C_CALL 被调用的Function对象。
PyTrace_C_EXCEPTION 被调用的Function对象。
PyTrace_C_RETURN 被调用的Function对象。
Name Value
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表示不需要创建参数 Tuples。 PCALL_FASTER_FUNCTION表示使用快速路径帧设置代码。

如果存在方法调用,可以pass更改参数 Tuples 并直接调用该函数来优化调用,则该方法将被记录两次。

仅当使用CALL_PROFILE定义的 Python 编译时,此Function才存在。

高级调试器支持

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

2.2 版中的新Function。

2.2 版中的新Function。

2.2 版中的新Function。

2.2 版中的新Function。

首页