Skip to main content

初始化,完成和线程

初始化和完成解释器

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)

如果 initsigs1,此函数的工作原理类似于 Py_Initialize()。如果 initsigs0,它跳过信号处理程序的初始化注册,这在嵌入Python时可能很有用。

int Py_IsInitialized()

当Python解释器已初始化时返回true(非零),否则返回false(零)。在 Py_FinalizeEx() 被调用之后,这返回false,直到 Py_Initialize() 被再次调用。

int Py_FinalizeEx()

撤销 Py_Initialize() 所做的所有初始化以及随后使用的Python/C API函数,并销毁自上次调用 Py_Initialize() 以来创建并未销毁的所有子解释器(见下面的 Py_NewInterpreter())。理想情况下,这释放了所有由Python解释器分配的内存。这是第二次呼叫时的无操作(没有先调用 Py_Initialize())。通常返回值为0.如果在最终化期间出现错误(刷新缓冲数据),则返回-1。

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

错误和警告: 模块中的模块和对象的破坏是以随机顺序完成的;这可能导致析构函数(__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()

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

wchar_t* Py_GetExecPrefix()

返回已安装的平台 dependent 文件的 exec-prefix。这是通过一些复杂的规则从使用 Py_SetProgramName() 设置的程序名和一些环境变量中导出的;例如,如果程序名称是 '/usr/local/bin/python',则exec-前缀是 '/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的英特尔机器是另一个平台,运行Linux的英特尔机器是另一个平台。同一操作系统的不同主要版本通常也形成不同的平台。非Unix操作系统是一个不同的故事;在这些系统上的安装策略是如此不同,前缀和exec前缀是无意义的,并设置为空字符串。注意,编译的Python字节码文件是平台无关的(但不是独立于编译它们的Python版本!)。

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

wchar_t* Py_GetProgramFullPath()

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

wchar_t* Py_GetPath()

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

void Py_SetPath(const wchar_t *)

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

这也使 sys.executable 只设置为原始程序名称(见 Py_SetProgramName()),sys.prefixsys.exec_prefix 为空。它是由调用者修改这些如果需要后调用 Py_Initialize()

使用 Py_DecodeLocale() 解码字节字符串以获取 wchar_* 字符串。

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

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)

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

如果 updatepath 是零,这是所有的功能。如果 updatepath 不为零,则该函数还根据以下算法修改 sys.path

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

  • 否则(也就是说,如果 argc0argv[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程序,有一个全局锁,称为 global interpreter lockGIL,它必须由当前线程持有,才能安全地访问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);

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

注解

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

非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() 的系统上,进程仅分叉发出叉的线程将存在。这也意味着由其他线程持有的任何锁永远不会被释放。 Python通过获取它在fork之前内部使用的锁,然后释放它们,为 os.fork() 解决这个问题。此外,它重置儿童中的任何 锁定对象。当扩展或嵌入Python时,没有办法通知Python需要在fork之前获取或在fork之后重置的额外(非Python)锁。 OS设备(如 pthread_atfork())需要用于完成同样的事情。此外,当扩展或嵌入Python时,直接调用 fork() 而不是通过 os.fork() (并返回或调用Python)可能导致死锁,其中一个Python的内部锁由在fork后停止的线程持有。 PyOS_AfterFork() 尝试重置必要的锁,但不总是能够。

高级API

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

PyInterpreterState

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

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

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() 的调用相匹配即可。通常,只要线程状态在Release()之前恢复到其先前的状态,则可以在 PyGILState_Ensure()PyGILState_Release() 调用之间使用其他线程相关的API。例如,Py_BEGIN_ALLOW_THREADSPy_END_ALLOW_THREADS 宏的正常使用是可以接受的。

返回值是调用 PyGILState_Ensure() 时线程状态的一个不透明的“句柄”,必须传递给 PyGILState_Release() 以确保Python处于相同的状态。即使允许递归调用,这些处理 cannot 也是共享的 - 每个对 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和 0,则返回 1。这个函数可以在任何时候从任何线程调用。只有它的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()
Return value: Borrowed reference.

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

int PyThreadState_SetAsyncExc(long id, PyObject *exc)

在线程中异步抛出异常。 id 参数是目标线程的线程ID; exc 是要引发的异常对象。此函数不会窃取对 exc 的任何引用。为了防止天真的滥用,你必须编写自己的C扩展来调用这个。必须用GIL调用。返回修改的线程状态的数量;这通常是一个,但如果没有找到线程id,它将为零。如果 excNULL,则线程的挂起异常(如果有)被清除。这没有例外。

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.stdinsys.stdoutsys.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)在使用子解释器时可能会被破坏。

异步通知

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

int Py_AddPendingCall(int (*func)(void *), void *arg)

调度要从主解释器线程调用的函数。成功时,返回 0,并且 func 排队等待在主线程中调用。失败时,将返回 -1,而不设置任何异常。

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

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

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

警告

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

3.1 新版功能.

分析和跟踪

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

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

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_RETURN 之一,arg 取决于 what 的值:

what 的价值

arg 的意义

PyTrace_CALL

总是 NULL

PyTrace_EXCEPTION

sys.exc_info() 返回的异常信息。

PyTrace_LINE

总是 NULL

PyTrace_RETURN

值返回到调用者,或 NULL (如果由异常引起)。

PyTrace_C_CALL

调用的函数对象。

PyTrace_C_EXCEPTION

调用的函数对象。

PyTrace_C_RETURN

调用的函数对象。

int PyTrace_CALL

当正在报告对函数或方法的新调用或者向生成器中的新条目时,what 参数对 Py_tracefunc 函数的值。注意,生成函数的迭代器的创建不报告,因为没有控制传递到相应帧中的Python字节码。

int PyTrace_EXCEPTION

出现异常时,Py_tracefunc 函数的 what 参数的值。当处理任何字节码之后,在正在执行的帧中设置异常之前,使用 what 的此值调用回调函数。这样做的效果是,作为异常传播导致Python栈解开,回调在异常传播时返回到每个帧时调用。只有跟踪函数接收这些事件;它们不需要profiler。

int PyTrace_LINE

当报告行号事件时,作为 what 参数传递给跟踪函数(但不是概要分析函数)的值。

int PyTrace_RETURN

当调用返回而不传播异常时,what 参数的值为 Py_tracefunc

int PyTrace_C_CALL

当要调用C函数时,Py_tracefunc 函数的 what 参数的值。

int PyTrace_C_EXCEPTION

当C函数引发异常时,what 参数的值将用于 Py_tracefunc 函数。

int PyTrace_C_RETURN

当C函数返回时,what 参数的值为 Py_tracefunc

void PyEval_SetProfile(Py_tracefunc func, PyObject *obj)

将分析器功能设置为 funcobj 参数作为其第一个参数传递给函数,可以是任何Python对象或 NULL。如果配置文件功能需要维护状态,为每个线程使用不同的 obj 值提供了一个方便和线程安全的地方来存储它。除了行号事件之外的所有监视事件都调用概要文件函数。

void PyEval_SetTrace(Py_tracefunc func, PyObject *obj)

将跟踪功能设置为 func。这类似于 PyEval_SetProfile(),除了跟踪功能没有接收到行号事件。

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 表示使用快速路径帧设置代码。

如果有一个方法调用,可以通过更改参数tuple并直接调用该函数来优化调用,则它会被记录两次。

这个函数只有在编译时使用 CALL_PROFILE 定义的Python才存在。

高级调试器支持

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

PyInterpreterState* PyInterpreterState_Head()

在所有这些对象的列表的头部返回解释器状态对象。

PyInterpreterState* PyInterpreterState_Next(PyInterpreterState *interp)

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

PyThreadState * PyInterpreterState_ThreadHead(PyInterpreterState *interp)

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

PyThreadState* PyThreadState_Next(PyThreadState *tstate)

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