初始化,完成和线程

初始化和完成解释器

  • 无效Py_InitializeEx(int * initsigs *)
    • 如果* initsigs 1,则此函数的作用类似于Py_Initialize()。如果 initsigs *为0,它将跳过 signal 处理程序的初始化注册,这在嵌入 Python 时可能很有用。

2.4 版的新Function。

  • int Py_IsInitialized()

    • 初始化 Python 解释器后,返回 true(非零),否则返回 false(零)。调用Py_Finalize()之后,返回 false,直到再次调用Py_Initialize()
  • 无效Py_Finalize()

    • 撤消由Py_Initialize()进行的所有初始化以及随后使用的 Python/C API 函数,并销毁自上次调用Py_Initialize()以来创建但尚未销毁的所有子解释器(请参阅下面的Py_NewInterpreter())。理想情况下,这将释放 Python 解释器分配的所有内存。第二次调用时此操作是空操作(无需先再次调用Py_Initialize())。没有返回值;终结期间的错误将被忽略。

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

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

Process-wide parameters

  • 无效Py_SetProgramName(char ** name *)
    • 如果完全调用此函数,则应在首次调用Py_Initialize()之前调用此函数。它告诉解释器程序的main()函数的argv[0]参数的值。 Py_GetPath()和下面的一些其他函数使用它来查找相对于解释器可执行文件的 Python 运行时库。默认值为'python'。该参数应指向静态存储中以零结尾的字符串,其内容在程序执行期间不会更改。 Python 解释器中的任何代码都不会更改此存储的内容。
  • 字符* Py_GetProgramName()
    • 返回使用Py_SetProgramName()设置的程序名称或默认值。返回的字符串指向静态存储;调用方不应修改其值。
  • 字符* Py_GetPrefix()

    • 返回* prefix *,用于安装与平台无关的文件。这是pass许多复杂的规则派生而来的,这些规则来自用Py_SetProgramName()设置的程序名称和一些环境变量。例如,如果程序名称为'/usr/local/bin/python',则前缀为'/usr/local'。返回的字符串指向静态存储;调用方不应修改其值。这对应于顶层Makefile中的 prefix 变量和构建时 configure 脚本的--prefix参数。该值可用于sys.prefix的 Python 代码。它仅在 Unix 上有用。另请参阅下一个Function。
  • 字符* Py_GetExecPrefix()

    • 返回已安装平台依赖文件的 exec-prefix。这是pass许多复杂的规则派生而来的,这些规则来自用Py_SetProgramName()设置的程序名称和一些环境变量。例如,如果程序名称为'/usr/local/bin/python',则 exec-prefix 为'/usr/local'。返回的字符串指向静态存储;调用方不应修改其值。这对应于顶层Makefile中的 exec_prefix 变量和构建时 configure 脚本的--exec-prefix参数。该值可用于sys.exec_prefix的 Python 代码。它仅在 Unix 上有用。

背景:当平台相关文件(例如可执行文件和共享库)安装在其他目录树中时,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是每个平台的不同文件系统。

  • 字符* Py_GetProgramFullPath()
    • 返回 Python 可执行文件的完整程序名称;这是从程序名称(由上面的Py_SetProgramName()设置)派生默认模块搜索路径的副作用而计算的。返回的字符串指向静态存储;调用方不应修改其值。该值可用于sys.executable的 Python 代码。
  • 字符* Py_GetPath()
    • 返回默认的模块搜索路径;这是根据程序名称(由上面的Py_SetProgramName()设置)和一些环境变量计算得出的。返回的字符串由一系列目录名称组成,这些目录名称由平台相关的分隔符分隔。分隔符在 Unix 和 Mac OS X 上为':',在 Windows 上为';'。返回的字符串指向静态存储;调用方不应修改其值。列表sys.path在解释器启动时用该值初始化;以后可以(通常是)对其进行修改,以更改加载模块的搜索路径。
  • const char * Py_GetVersion()
    • 返回此 Python 解释器的版本。这是一个看起来像的字符串
"1.5 (#67, Dec 31 1997, 22:34:28) [GCC 2.7.2.2]"

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

  • const char * Py_GetPlatform()
    • 返回当前平台的平台标识符。在 Unix 上,这是由 os 的“正式”名称组成的,转换为小写形式,后跟主版本号。例如,对于 Solaris 2.x(也称为 SunOS 5.x),该值为'sunos5'。在 Mac OS X 上,它是'darwin'。在 Windows 上,它是'win'。返回的字符串指向静态存储;调用方不应修改其值。该值可用于sys.platform的 Python 代码。
  • const char * Py_GetCopyright()
    • 返回当前 Python 版本的官方版权字符串,例如

'Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam'

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

  • 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 代码。

  • 无效PySys_SetArgvEx(int * argc *,char *** argv *,int * updatepath *)
    • 根据* argc argv 设置sys.argv。这些参数与传递给程序的main()函数的参数类似,不同之处在于,第一个条目应引用要执行的脚本文件,而不是托管 Python 解释器的可执行文件。如果没有要运行的脚本,则 argv *中的第一个条目可以为空字符串。如果此函数未能初始化sys.argv,则使用Py_FatalError()发出致命情况 signal。

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

  • 如果在argv[0]中传递了现有脚本的名称,则该脚本所在目录的绝对路径将以sys.path开头。

  • 否则(即,如果* argc *为 0 或argv[0]不指向现有文件名),则在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。

  • 无效PySys_SetArgv(int * argc *,char *** argv *)

  • 无效Py_SetPythonHome(char ** home *)

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

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

  • 字符* Py_GetPythonHome()

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

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 解释器时最常用的类型和函数:

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

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

  • PyThreadState

    • 此数据结构表示单个线程的状态。唯一的公共数据成员是PyInterpreterState * interp,它指向该线程的解释器状态。
  • 无效PyEval_InitThreads()

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

Note

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

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

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

  • int PyEval_ThreadsInitialized()
    • 如果已调用PyEval_InitThreads(),则返回非零值。可以在不持有 GIL 的情况下调用此函数,因此可以在运行单线程时避免调用锁定 API。在编译时禁用线程支持时,此Function不可用。

2.4 版的新Function。

  • PyThreadState* PyEval_SaveThread ( )

    • 释放全局解释器锁(如果已创建它并启用了线程支持),然后将线程状态重置为* NULL ,返回先前的线程状态(不是 NULL *)。如果已创建锁,则当前线程必须已获取它。 (即使在编译时禁用了线程支持,此Function也可用.)
  • 无效PyEval_RestoreThread(PyThreadState ** tstate *)

    • 获取全局解释器锁(如果已创建它并且启用了线程支持),并将线程状态设置为* tstate ,该状态不能为 NULL *。如果已创建锁,则当前线程一定不能获取它,否则将导致死锁。 (即使在编译时禁用了线程支持,此Function也可用.)
  • PyThreadState* PyThreadState_Get ( )

    • 返回当前线程状态。全局解释器锁必须保持。当前线程状态为* NULL 时,这将引发致命错误(以便调用者无需检查 NULL *)。
  • PyThreadState * PyThreadState_Swap(PyThreadState ** tstate *)

    • 用参数* tstate 给定的线程状态交换当前线程状态,该状态可能是 NULL *。全局解释器锁必须保持并且不能释放。
  • 无效PyEval_ReInitThreads()

    • PyOS_AfterFork()调用此函数,以确保新创建的子进程不持有引用未在子进程中运行的线程的锁。

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

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

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

2.3 版的新Function。

  • 无效PyGILState_Release(PyGILState_STATE)
    • 释放以前获取的所有资源。在此调用之后,Python 的状态将与相应的PyGILState_Ensure()调用之前的状态相同(但通常此状态对于调用者而言将是未知的,因此使用了 GILState API)。

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

2.3 版的新Function。

  • PyThreadState* PyGILState_GetThisThreadState ( )
    • 获取此线程的当前线程状态。如果当前线程上未使用 GILState API,则可能返回NULL。请注意,即使未在主线程上进行自动线程状态调用,主线程也始终具有这种线程状态。这主要是辅助Function/诊断Function。

2.3 版的新Function。

以下宏通常不带尾部分号使用;在 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,但不带大括号和变量语句。在编译时禁用线程支持时,此操作为空。

Low-level API

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

  • PyInterpreterState* PyInterpreterState_New ( )

    • 创建一个新的解释器状态对象。全局解释器锁不需要保留,但如果有必要序列化对此函数的调用,则可以保留。
  • 无效PyInterpreterState_Clear(PyInterpreterState ** interp *)

    • 重置解释器状态对象中的所有信息。全局解释器锁必须保持。
  • 无效PyInterpreterState_Delete(PyInterpreterState ** interp *)

    • 销毁解释器状态对象。全局解释器锁不需要保持。必须先pass调用PyInterpreterState_Clear()重置解释器状态。
  • PyThreadState * PyThreadState_New(PyInterpreterState ** interp *)

    • 创建属于给定解释器对象的新线程状态对象。全局解释器锁不需要保留,但如果有必要序列化对此函数的调用,则可以保留。
  • 无效PyThreadState_Clear(PyThreadState ** tstate *)

    • 重置线程状态对象中的所有信息。全局解释器锁必须保持。
  • 无效PyThreadState_Delete(PyThreadState ** tstate *)

    • 销毁线程状态对象。全局解释器锁不需要保持。必须pass先前对PyThreadState_Clear()的调用来重置线程状态。
  • PyObject* PyThreadState_GetDict ( )

    • *返回值:借用参考。

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

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

  • int PyThreadState_SetAsyncExc(long * id *,PyObject ** exc *)
    • 在线程中异步引发异常。 * id *参数是目标线程的线程 ID; * exc 是要引发的异常对象。此函数不会窃取对 exc 的任何引用。为避免天真的滥用,您必须编写自己的 Cextensions 来调用它。必须在保持 GIL 的情况下调用。返回修改后的线程状态数;通常为 1,但如果未找到线程 ID,则为零。如果 exc *为NULL,则清除线程的挂起异常(如果有)。这没有 exception。

2.3 版的新Function。

  • 无效PyEval_AcquireThread(PyThreadState ** tstate *)
    • 获取全局解释器锁,并将当前线程状态设置为* tstate ,不应为 NULL *。锁必须早先创建。如果此线程已具有锁,则会发生死锁。

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

  • 无效PyEval_ReleaseThread(PyThreadState ** tstate *)
    • 将当前线程状态重置为* NULL *并释放全局解释器锁。该锁必须是较早创建的,并且必须由当前线程持有。 * tstate 参数(不能为 NULL *)仅用于检查它是否表示当前线程状态-如果不是,则报告致命错误。

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

  • 无效PyEval_AcquireLock()
    • 获取全局解释器锁。锁必须早先创建。如果此线程已具有锁,则会发生死锁。

Warning

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

  • 无效PyEval_ReleaseLock()
    • 释放全局解释器锁。锁必须早先创建。

Warning

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

Sub-interpreter support

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

  • PyThreadState* Py_NewInterpreter ( )
    • 创建一个新的子解释器。这是一个(几乎)完全独立的环境,用于执行 Python 代码。特别是,新解释器对所有导入的模块(包括基本模块builtinsmainsys)具有单独的独立版本。装入的模块表(sys.modules)和模块搜索路径(sys.path)也分开。新环境没有sys.argv变量。它具有新的标准 I/O 流文件对象sys.stdinsys.stdoutsys.stderr(但是它们引用相同的基础文件 Descriptors)。

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

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

  • 无效Py_EndInterpreter(PyThreadState ** tstate *)
    • 销毁由给定线程状态表示的(子)解释器。给定的线程状态必须是当前线程状态。请参阅下面的线程状态讨论。调用返回时,当前线程状态为* NULL *。与该解释器关联的所有线程状态均被销毁。 (全局解释器锁必须在调用此函数之前保持,并在返回时仍保持.)Py_Finalize()将销毁当时尚未明确销毁的所有子解释器。

错误和警告

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

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

Asynchronous Notifications

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

  • int Py_AddPendingCall(int( **func )(void ),void arg *)
    • 安排要从主解释器线程调用的函数。成功后,将返回0并将* func *排队 await 在主线程中被调用。失败时,将返回-1且未设置任何异常。

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

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

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

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的事件相同。

  • int (*Py_tracefunc)(PyObject *obj ,PyFrameObject frame *,int * what *,PyObject ** arg *)
    • 使用PyEval_SetProfile()PyEval_SetTrace()注册的跟踪Function的类型。第一个参数是以* obj 的形式传递给注册函数的对象, frame 是事件所属的框架对象, what 是常量PyTrace_CALLPyTrace_EXCEPTIONPyTrace_LINEPyTrace_RETURNPyTrace_C_CALLPyTrace_C_EXCEPTIONPyTrace_C_RETURN arg 取决于 what *的值:
什么的价值* arg *的含义
PyTrace_CALLAlways Py_None.
PyTrace_EXCEPTIONsys.exc_info()返回的异常信息。
PyTrace_LINEAlways Py_None.
PyTrace_RETURN返回给调用方的值,如果是由异常引起的,则为* NULL *。
PyTrace_C_CALL被调用的Function对象。
PyTrace_C_EXCEPTION被调用的Function对象。
PyTrace_C_RETURN被调用的Function对象。
  • 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 *参数值。
  • 无效PyEval_SetProfile(Py_tracefunc * func *,PyObject ** obj *)

    • 将探查器Function设置为* func *。 * obj 参数作为第一个参数传递给函数,并且可以是任何 Python 对象或 NULL 。如果概要文件Function需要维护状态,则为每个线程使用不同的 obj *值可提供方便且线程安全的位置来存储它。对于除PyTrace_LINEPyTrace_EXCEPTION之外的所有受监视事件,将调用概要文件Function。
  • 无效PyEval_SetTrace(Py_tracefunc * func *,PyObject ** obj *)

    • 将跟踪Function设置为* func 。这类似于PyEval_SetProfile(),除了跟踪函数确实接收行号事件并且不接收与被调用的 C 函数对象有关的任何事件。使用PyEval_SetTrace()注册的任何跟踪函数都不会接收PyTrace_C_CALLPyTrace_C_EXCEPTIONPyTrace_C_RETURN作为 what *参数的值。
  • PyObject * PyEval_GetCallStats(PyObject *自己)

    • 返回函数调用计数的 Tuples。为 Tuples 中的位置定义了常量:
NameValue
PCALL_ALL0
PCALL_FUNCTION1
PCALL_FAST_FUNCTION2
PCALL_FASTER_FUNCTION3
PCALL_METHOD4
PCALL_BOUND_METHOD5
PCALL_CFUNCTION6
PCALL_TYPE7
PCALL_GENERATOR8
PCALL_OTHER9
PCALL_POP10

PCALL_FAST_FUNCTION表示不需要创建参数 Tuples。 PCALL_FASTER_FUNCTION表示使用快速路径帧设置代码。

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

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

高级调试器支持

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

  • PyInterpreterState* PyInterpreterState_Head ( )
    • 返回所有此类对象列表顶部的解释器状态对象。

2.2 版中的新Function。

2.2 版中的新Function。

2.2 版中的新Function。

2.2 版中的新Function。