初始化、终结和线程

也见 Python Initialization Configuration .

在python初始化之前

在嵌入python的应用程序中, Py_Initialize() 在使用任何其他python/c api函数之前必须调用函数;除了一些函数和 global configuration variables .

在初始化python之前,可以安全地调用以下函数:

全局配置变量

python有用于全局配置的变量来控制不同的特性和选项。默认情况下,这些标志由 command line options .

当某个标志由某个选项设置时,该标志的值是该选项设置的次数。例如, -b 集合 Py_BytesWarningFlag 至1和 -bb 集合 Py_BytesWarningFlag 到2。

int Py_BytesWarningFlag

比较时发出警告 bytesbytearray 具有 strbytes 具有 int .如果大于或等于,则发出错误 2 .

-b 选择权。

int Py_DebugFlag

打开解析器调试输出(仅适用于专家,具体取决于编译选项)。

-d 期权与期权 PYTHONDEBUG 环境变量。

int Py_DontWriteBytecodeFlag

如果设置为非零,则python不会尝试写入 .pyc 导入源模块时的文件。

-B 期权与期权 PYTHONDONTWRITEBYTECODE 环境变量。

int Py_FrozenFlag

在中计算模块搜索路径时禁止显示错误消息 Py_GetPath() .

私有标志由使用 _freeze_importlibfrozenmain 程序。

int Py_HashRandomizationFlag

设置为 1 如果 PYTHONHASHSEED 环境变量设置为非空字符串。

如果标志非零,则读取 PYTHONHASHSEED 用于初始化机密hash种子的环境变量。

int Py_IgnoreEnvironmentFlag

忽略所有 PYTHON* 环境变量,例如 PYTHONPATHPYTHONHOME ,可以设置。

-E-I 选项。

int Py_InspectFlag

当脚本作为第一个参数或 -c 使用选项,在执行脚本或命令后进入交互模式,即使在 sys.stdin 似乎不是终端。

-i 期权与期权 PYTHONINSPECT 环境变量。

int Py_InteractiveFlag

-i 选择权。

int Py_IsolatedFlag

以隔离模式运行python。在隔离模式下 sys.path 既不包含脚本的目录,也不包含用户的站点包目录。

-I 选择权。

3.4 新版功能.

int Py_LegacyWindowsFSEncodingFlag

如果该标志为非零,则使用 mbcs 编码使用 replace 错误处理程序,而不是使用 surrogatepass 错误处理程序,用于 filesystem encoding and error handler

设置为 1 如果 PYTHONLEGACYWINDOWSFSENCODING 环境变量设置为非空字符串。

PEP 529 了解更多详细信息。

Availability :Windows。

int Py_LegacyWindowsStdioFlag

如果标志非零,则使用 io.FileIO 而不是 WindowsConsoleIO 对于 sys 标准流。

设置为 1 如果 PYTHONLEGACYWINDOWSSTDIO 环境变量设置为非空字符串。

PEP 528 了解更多详细信息。

Availability :Windows。

int Py_NoSiteFlag

禁用模块导入 site 以及 sys.path 这是必然的。如果 site 稍后显式导入(调用 site.main() 如果你想触发它们)。

-S 选择权。

int Py_NoUserSiteDirectory

不要添加 user site-packages directorysys.path .

-s-I 选项,以及 PYTHONNOUSERSITE 环境变量。

int Py_OptimizeFlag

-O 期权与期权 PYTHONOPTIMIZE 环境变量。

int Py_QuietFlag

即使在交互模式下也不显示版权和版本消息。

-q 选择权。

3.2 新版功能.

int Py_UnbufferedStdioFlag

强制取消缓冲stdout和stderr流。

-u 期权与期权 PYTHONUNBUFFERED 环境变量。

int Py_VerboseFlag

每次初始化模块时打印一条消息,显示从中加载模块的位置(文件名或内置模块)。如果大于或等于 2 ,为搜索模块时选中的每个文件打印一条消息。还提供有关退出时模块清理的信息。

-v 期权与期权 PYTHONVERBOSE 环境变量。

初始化和完成解释器

void Py_Initialize()

初始化python解释器。在嵌入python的应用程序中,应该在使用任何其他python/c api函数之前调用此函数;请参见 Before Python Initialization 为数不多的例外。

这将初始化已加载模块的表 (sys.modules )并创建基本模块 builtins__main__sys . 它还初始化模块搜索路径 (sys.path )它没有设置 sys.argv 使用 PySys_SetArgvEx() 为此。这是第二次调用时的禁止操作(不调用 Py_FinalizeEx() 首先)。没有返回值;如果初始化失败,则是一个致命错误。

注解

在Windows上,更改控制台模式 O_TEXTO_BINARY 这也将影响使用C运行时的控制台的非python使用。

void Py_InitializeEx(int initsigs)

此函数的工作方式如下 Py_Initialize() 如果 因特西格斯1 .如果 因特西格斯0 它跳过了信号处理程序的初始化注册,这在嵌入Python时可能很有用。

int Py_IsInitialized()

初始化python解释器后返回true(非零),否则返回false(零)。后 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分配的所有内存。

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

提出一个 auditing event cpython._PySys_ClearAuditHooks 没有参数。

3.6 新版功能.

void Py_Finalize()

这是的向后兼容版本 Py_FinalizeEx() 忽略返回值。

全过程参数

int Py_SetStandardStreamEncoding(const char *encoding, const char *errors)

此函数应该在 Py_Initialize() ,如果它被调用。它指定与标准IO一起使用的编码和错误处理,其含义与中的相同。 str.encode() .

它超越 PYTHONIOENCODING 值,并允许嵌入代码在环境变量不工作时控制IO编码。

编码 和/或 错误 可能是 NULL 使用 PYTHONIOENCODING 和/或默认值(取决于其他设置)。

注意 sys.stderr 始终使用“backslashreplace”错误处理程序,而不考虑此(或任何其他)设置。

如果 Py_FinalizeEx() 如果调用,则需要再次调用此函数,以影响后续对 Py_Initialize() .

返回 0 如果成功,则为出错时的非零值(例如,在解释器已初始化后调用)。

3.4 新版功能.

void Py_SetProgramName(const wchar_t *name)

此函数应该在 Py_Initialize() 第一次被调用,如果它被调用的话。它告诉解释器 argv[0] 参数 main() 程序的功能(转换为宽字符)。这是由 Py_GetPath() 以及下面的一些其他函数来查找与解释器可执行文件相关的python运行时库。默认值为 'python' . 该参数应指向静态存储器中以零结尾的宽字符串,该字符串的内容在程序执行期间不会更改。python解释器中的任何代码都不会更改此存储的内容。

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

wchar *Py_GetProgramName()

返回用设置的程序名 Py_SetProgramName() 或默认值。返回的字符串指向静态存储;调用方不应修改其值。

在此之前不应调用此函数 Py_Initialize() ,否则它将返回 NULL

在 3.10 版更改: 现在它又回来了 NULL 如果在此之前调用 Py_Initialize()

wchar_t *Py_GetPrefix()

返回 前缀 用于安装的独立于平台的文件。这是通过许多复杂的规则从程序名集合中派生的 Py_SetProgramName() 以及一些环境变量;例如,如果程序名是 '/usr/local/bin/python' ,前缀是 '/usr/local' . 返回的字符串指向静态存储;调用方不应修改其值。这对应于 prefix 顶层变量 Makefile 以及 --prefix 参数 configure 在生成时编写脚本。该值可用于python代码 sys.prefix .它只在Unix上有用。另请参见下一个函数。

在此之前不应调用此函数 Py_Initialize() ,否则它将返回 NULL

在 3.10 版更改: 现在它又回来了 NULL 如果在此之前调用 Py_Initialize()

wchar_t *Py_GetExecPrefix()

返回 exec-prefix 对于已安装的平台- 依赖的 文件夹。这是通过许多复杂的规则从程序名集合中派生的 Py_SetProgramName() 以及一些环境变量;例如,如果程序名是 '/usr/local/bin/python' ,exec前缀为 '/usr/local' . 返回的字符串指向静态存储;调用方不应修改其值。这对应于 exec_prefix 顶层变量 Makefile 以及 --exec-prefix 参数 configure 在生成时编写脚本。该值可用于python代码 sys.exec_prefix .它只在Unix上有用。

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

一般来说,平台是硬件和软件系列的组合,例如运行Solaris 2.x操作系统的SPARC机器被认为是相同的平台,但运行Solaris 2.x的Intel机器是另一个平台,运行Linux的Intel机器是另一个平台。同一操作系统的不同主要版本通常也会形成不同的平台。非UNIX操作系统是另一回事;这些系统上的安装策略是如此不同,以至于前缀和exec前缀毫无意义,并设置为空字符串。请注意,编译后的python字节码文件与平台无关(但与编译它们的python版本无关!).

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

在此之前不应调用此函数 Py_Initialize() ,否则它将返回 NULL

在 3.10 版更改: 现在它又回来了 NULL 如果在此之前调用 Py_Initialize()

wchar_t *Py_GetProgramFullPath()

返回python可执行文件的完整程序名;这是从程序名(由 Py_SetProgramName() 以上)。返回的字符串指向静态存储;调用方不应修改其值。该值可用于python代码 sys.executable .

在此之前不应调用此函数 Py_Initialize() ,否则它将返回 NULL

在 3.10 版更改: 现在它又回来了 NULL 如果在此之前调用 Py_Initialize()

wchar_t *Py_GetPath()

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

在此之前不应调用此函数 Py_Initialize() ,否则它将返回 NULL

在 3.10 版更改: 现在它又回来了 NULL 如果在此之前调用 Py_Initialize()

void Py_SetPath(const wchar_t*)

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

这也导致 sys.executable 设置为程序完整路径(请参见 Py_GetProgramFullPath()sys.prefixsys.exec_prefix 是空的。如果在调用后需要修改这些内容,则由调用方决定。 Py_Initialize() .

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

路径参数是在内部复制的,因此调用方可以在调用完成后释放它。

在 3.8 版更改: 程序完整路径现在用于 sys.executable ,而不是程序名。

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]"

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

const char *Py_GetBuildInfo()

返回有关当前python解释器实例的序列号和生成日期和时间的信息,例如:

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

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

void PySys_SetArgvEx(int argc, wchar_t **argv, int updatepath)

集合 sys.argv 基于 argcargv . 这些参数与传递给程序的参数类似 main() 不同的是,第一个条目应该引用要执行的脚本文件,而不是宿主Python解释器的可执行文件。如果没有将要运行的脚本,则 argv 可以是空字符串。如果此函数未能初始化 sys.argv ,使用 Py_FatalError() .

如果 更新数据表 是零,这就是函数的全部功能。如果 更新数据表 为非零,函数还修改 sys.path 根据以下算法:

  • 如果传入了现有脚本的名称 argv[0] ,脚本所在目录的绝对路径是 sys.path .

  • 否则(即,如果 argc0argv[0] 不指向现有的文件名),空字符串被附加到 sys.path ,与当前工作目录的前置相同 ("."

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

注解

建议将python解释器嵌入的应用程序用于执行单个脚本传递以外的目的。 0 作为 更新数据表 和更新 sys.path 如果愿意的话。见 CVE-2008-5983 .

在3.1.3之前的版本上,您可以通过手动弹出第一个 sys.path 调用后的元素 PySys_SetArgv() ,例如使用::

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

3.1.3 新版功能.

void PySys_SetArgv(int argc, wchar_t **argv)

此函数的工作方式如下 PySys_SetArgvEx() 具有 更新数据表 设置为 1 除非 python 解释程序是用 -I .

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

在 3.4 版更改: 这个 更新数据表 价值取决于 -I .

void Py_SetPythonHome(const wchar_t *home)

设置默认的“home”目录,即标准python库的位置。见 PYTHONHOME 参数字符串的含义。

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

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

w_char *Py_GetPythonHome()

返回默认的“home”,即以前调用设置的值 Py_SetPythonHome()PYTHONHOME 环境变量(如果已设置)。

在此之前不应调用此函数 Py_Initialize() ,否则它将返回 NULL

在 3.10 版更改: 现在它又回来了 NULL 如果在此之前调用 Py_Initialize()

线程状态和全局解释器锁

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 宏关闭块。

上面的块扩展到以下代码:

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,最后存储线程状态指针,然后才能开始使用tPython/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。

关于fork()的警告

关于线程的另一个重要注意事项是它们在面对C时的行为 fork() 打电话。在大多数系统上 fork() ,在进程fork之后,将只存在发出fork的线程。这对必须如何处理锁以及CPython运行时中的所有存储状态都有具体的影响。

只有“当前”线程仍然存在,这意味着其他线程持有的任何锁都不会被释放。Python解决了这个问题 os.fork() 通过获取它在分叉之前内部使用的锁,然后释放它们。此外,它会重置任何 锁定对象 在孩子身上。在扩展或嵌入python时,无法通知python在fork之前或之后需要获取的额外(非python)锁。操作系统设施,如 pthread_atfork() 需要用来完成同样的事情。另外,当扩展或嵌入python时,调用 fork() 直接而不是通过 os.fork() (并返回或调用python)可能会导致死锁,死锁是由一个线程持有的,该线程在fork之后已失效。 PyOS_AfterFork_Child() 尝试重置必要的锁,但不总是能够重置。

所有其他线程都消失的事实也意味着必须正确地清理CPython的运行时状态,这 os.fork() 做。这意味着完成所有其他 PyThreadState 属于当前解释器和所有其他解释器的对象 PyInterpreterState 物体。由于这一点和 "main" interpreterfork() 只应在解释器的“主”线程中调用,CPython全局运行时最初是在该线程中初始化的。唯一的例外是 exec() 稍后将立即呼叫。

高级API

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

type PyInterpreterState

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

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

type PyThreadState

This data structure represents the state of a single thread. The only public data member is interp (PyInterpreterState*), which points to this thread's interpreter state.

void PyEval_InitThreads()

不推荐的函数,它什么也不做。

在Python3.6及更高版本中,如果GIL不存在,则此函数创建它。

在 3.9 版更改: 函数现在什么也不做。

在 3.7 版更改: 此函数现在由调用 Py_Initialize() 所以你不必再叫它自己了。

在 3.2 版更改: 在此之前不能调用此函数 Py_Initialize() 不再。

Deprecated since version 3.9, will be removed in version 3.11.

int PyEval_ThreadsInitialized()

返回一个非零值,如果 PyEval_InitThreads() 已被调用。可以在不保留gil的情况下调用此函数,因此可以在运行单线程时避免调用锁定API。

在 3.7 版更改: 这个 GIL 现在由初始化 Py_Initialize() .

Deprecated since version 3.9, will be removed in version 3.11.

PyThreadState *PyEval_SaveThread()

释放全局解释器锁(如果已创建)并将线程状态重置为 NULL ,返回上一个线程状态(不是 NULL )如果已创建锁,则当前线程必须已获取该锁。

void PyEval_RestoreThread(PyThreadState *tstate)

获取全局解释器锁(如果已创建)并将线程状态设置为 泰州 ,不能是 NULL . 如果已经创建了锁,则当前线程不能获取它,否则将导致死锁。

注解

在运行时完成时从线程调用此函数将终止线程,即使该线程不是由python创建的。你可以使用 _Py_IsFinalizing()sys.is_finalizing() 在调用此函数之前检查解释器是否正在完成,以避免不必要的终止。

PyThreadState *PyThreadState_Get()

返回当前线程状态。必须持有全局解释器锁。当当前线程状态为 NULL ,这将发出一个致命错误(以便调用方不需要检查 NULL

PyThreadState *PyThreadState_Swap(PyThreadState *tstate)

将当前线程状态与参数给定的线程状态交换 泰州 ,可能是 NULL . 全局解释器锁必须保持不释放。

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

PyGILState_STATE PyGILState_Ensure()

确保当前线程准备好调用python c api,而不管python或全局解释器锁的当前状态如何。只要每个调用与对的调用匹配,线程就可以根据需要多次调用它。 PyGILState_Release() .通常,在 PyGILState_Ensure()PyGILState_Release() 只要线程状态恢复到释放()之前的状态,就调用。例如,正常使用 Py_BEGIN_ALLOW_THREADSPy_END_ALLOW_THREADS 宏是可接受的。

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

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

注解

在运行时完成时从线程调用此函数将终止线程,即使该线程不是由python创建的。你可以使用 _Py_IsFinalizing()sys.is_finalizing() 在调用此函数之前检查解释器是否正在完成,以避免不必要的终止。

void PyGILState_Release(PyGILState_STATE)

释放以前获得的任何资源。调用后,python的状态将与对应的 PyGILState_Ensure() 调用(但通常调用方不知道此状态,因此使用Gilstate API)。

每次召唤 PyGILState_Ensure() 必须通过调用匹配 PyGILState_Release() 在同一条线上。

PyThreadState *PyGILState_GetThisThreadState()

获取此线程的当前线程状态。可能返回 NULL 如果当前线程上没有使用Gilstate API。注意,主线程总是有这样的线程状态,即使没有对主线程进行自动线程状态调用。这主要是辅助/诊断功能。

int PyGILState_Check()

返回 1 如果当前线程持有gil,并且 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

必须在之后调用以下所有函数 Py_Initialize() .

在 3.7 版更改: Py_Initialize() 现在初始化 GIL .

PyInterpreterState *PyInterpreterState_New()

创建新的解释器状态对象。不需要持有全局解释器锁,但如果需要序列化对此函数的调用,则可以持有全局解释器锁。

提出一个 auditing event cpython.PyInterpreterState_New 没有参数。

void PyInterpreterState_Clear(PyInterpreterState *interp)

重置解释器状态对象中的所有信息。必须持有全局解释器锁。

提出一个 auditing event cpython.PyInterpreterState_Clear 没有参数。

void PyInterpreterState_Delete(PyInterpreterState *interp)

销毁解释器状态对象。不需要持有全局解释器锁。解释器状态必须通过以前的调用重置 PyInterpreterState_Clear() .

PyThreadState *PyThreadState_New(PyInterpreterState *interp)

创建属于给定解释器对象的新线程状态对象。不需要持有全局解释器锁,但如果需要序列化对此函数的调用,则可以持有全局解释器锁。

void PyThreadState_Clear(PyThreadState *tstate)

重置线程状态对象中的所有信息。必须持有全局解释器锁。

在 3.9 版更改: 此函数现在调用 PyThreadState.on_delete 回拨。以前,那发生在 PyThreadState_Delete() .

void PyThreadState_Delete(PyThreadState *tstate)

销毁线程状态对象。不需要持有全局解释器锁。线程状态必须已用以前的调用重置 PyThreadState_Clear() .

void PyThreadState_DeleteCurrent(void)

销毁当前线程状态并释放全局解释器锁。就像 PyThreadState_Delete() ,不需要保持全局解释器锁。线程状态必须已用以前的调用重置 PyThreadState_Clear() .

PyFrameObject *PyThreadState_GetFrame(PyThreadState *tstate)

获取Python线程状态的当前帧 泰州 .

返回一个 strong reference 。返回 NULL 如果当前没有帧正在执行,则返回。

也见 PyEval_GetFrame() .

泰州 不得 NULL .

3.9 新版功能.

uint64_t PyThreadState_GetID(PyThreadState *tstate)

获取Python线程状态的唯一线程状态标识符 泰州 .

泰州 不得 NULL .

3.9 新版功能.

PyInterpreterState *PyThreadState_GetInterpreter(PyThreadState *tstate)

获取Python线程状态的解释器 泰州 .

泰州 不得 NULL .

3.9 新版功能.

PyInterpreterState *PyInterpreterState_Get(void)

找到当前的翻译。

如果没有当前的Python线程状态或当前的解释器,则发出致命错误。它不能返回空值。

打电话的人必须拿着电话。

3.9 新版功能.

int64_t PyInterpreterState_GetID(PyInterpreterState *interp)

返回解释器的唯一ID。如果执行此操作时出错,则 -1 返回并设置错误。

打电话的人必须拿着电话。

3.7 新版功能.

PyObject *PyInterpreterState_GetDict(PyInterpreterState *interp)

返回一个可以存储解释程序特定数据的字典。如果此函数返回 NULL 然后就不会引发异常,调用方应该假设没有解释程序特定的dict可用。

这不是替代 PyModule_GetState() ,应该使用哪些扩展来存储解释程序特定的状态信息。

3.8 新版功能.

typedef PyObject *(*_PyFrameEvalFunction)(PyThreadState *tstate, PyFrameObject *frame, int throwflag)

帧计算函数的类型。

这个 投掷旗 参数由 throw() 生成器方法:如果非零,则处理当前异常。

在 3.9 版更改: 函数现在需要 泰州 参数。

_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()
Return value: Borrowed reference.

返回扩展可以存储线程特定状态信息的字典。每个扩展都应该使用一个唯一的键来在字典中存储状态。当当前线程状态不可用时,可以调用此函数。如果此函数返回 NULL ,未引发异常,调用方应假定当前线程状态不可用。

int PyThreadState_SetAsyncExc(unsigned long id, PyObject *exc)

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

在 3.7 版更改: 类型 id 参数更改自 longunsigned long .

void PyEval_AcquireThread(PyThreadState *tstate)

获取全局解释器锁并将当前线程状态设置为 泰州 ,不能是 NULL . 锁必须在前面创建。如果这个线程已经有了锁,就会发生死锁。

注解

在运行时完成时从线程调用此函数将终止线程,即使该线程不是由python创建的。你可以使用 _Py_IsFinalizing()sys.is_finalizing() 在调用此函数之前检查解释器是否正在完成,以避免不必要的终止。

在 3.8 版更改: 更新以符合 PyEval_RestoreThread()Py_END_ALLOW_THREADS()PyGILState_Ensure() ,并在解释器完成时终止当前线程(如果调用)。

PyEval_RestoreThread() 是一个更高级别的函数,它始终可用(即使线程尚未初始化)。

void PyEval_ReleaseThread(PyThreadState *tstate)

将当前线程状态重置为 NULL 释放全局解释器锁。锁必须在前面创建,并且必须由当前线程持有。这个 泰州 参数,不能是 NULL ,仅用于检查它是否代表当前线程状态—如果不是,则报告致命错误。

PyEval_SaveThread() 是一个更高级别的函数,它始终可用(即使线程尚未初始化)。

void PyEval_AcquireLock()

获取全局解释器锁。锁必须在前面创建。如果这个线程已经有了锁,就会发生死锁。

3.2 版后已移除: 此函数不更新当前线程状态。请使用 PyEval_RestoreThread()PyEval_AcquireThread() 相反。

注解

在运行时完成时从线程调用此函数将终止线程,即使该线程不是由python创建的。你可以使用 _Py_IsFinalizing()sys.is_finalizing() 在调用此函数之前检查解释器是否正在完成,以避免不必要的终止。

在 3.8 版更改: 更新以符合 PyEval_RestoreThread()Py_END_ALLOW_THREADS()PyGILState_Ensure() ,并在解释器完成时终止当前线程(如果调用)。

void PyEval_ReleaseLock()

释放全局解释器锁。锁必须在前面创建。

3.2 版后已移除: 此函数不更新当前线程状态。请使用 PyEval_SaveThread()PyEval_ReleaseThread() 相反。

辅助翻译支持

虽然在大多数情况下,您将只嵌入一个Python解释器,但在某些情况下,您需要在同一进程中甚至在同一线程中创建几个独立的解释器。口译员允许你这样做。

“main”解释器是运行时初始化时创建的第一个解释器。它通常是进程中唯一的Python解释器。与子口译员不同,主口译员具有独特的过程全局职责,如信号处理。它还负责运行时初始化期间的执行,通常是运行时终结期间的活动解释器。这个 PyInterpreterState_Main() 函数返回指向其状态的指针。

您可以使用 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函数不同,在进入时不需要有当前的线程状态。)

扩展模块在(子)口译员之间共享,如下所示:

  • 对于使用多阶段初始化的模块,例如。 PyModule_FromDefAndSpec() ,为每个解释器创建并初始化一个单独的模块对象。这些模块对象之间只共享C级静态和全局变量。

  • 对于使用单相初始化的模块,例如。 PyModule_Create() ,第一次导入特定扩展时,它将正常初始化,并将其模块字典的(浅层)副本存储起来。当同一个扩展被另一个(子)解释器导入时,将初始化一个新模块,并用该副本的内容填充该模块;扩展的 init 函数未被调用。因此,模块字典中的对象最终会在(子)解释器之间共享,这可能会导致不需要的行为(请参阅 Bugs and caveats 下面)。

    注意,这与通过调用 Py_FinalizeEx()Py_Initialize() ;在这种情况下,扩展 initmodule 功能 is 又打电话来了。与多阶段初始化一样,这意味着这些模块之间只共享C级静态和全局变量。

void Py_EndInterpreter(PyThreadState *tstate)

销毁由给定线程状态表示的(子)解释器。给定的线程状态必须是当前线程状态。请参阅下面关于线程状态的讨论。当调用返回时,当前线程状态为 NULL . 与此解释器关联的所有线程状态都将被销毁。(全局解释器锁必须在调用此函数之前保持不变,当它返回时仍保持不变。) Py_FinalizeEx() 将销毁当时未明确销毁的所有子解释程序。

错误和警告

因为子解释器(和主解释器)是同一过程的一部分,所以它们之间的隔离并不完美——例如,使用诸如 os.close() 它们可能(意外地或恶意地)影响彼此打开的文件。由于在(子)解释器之间共享扩展的方式,某些扩展可能无法正常工作;这在使用单相初始化或(静态)全局变量时尤其可能。可以将在一个子解释器中创建的对象插入到另一个(子)解释器的命名空间中;如果可能,应该避免这样做。

应特别注意避免在子解释器之间共享用户定义的函数、方法、实例或类,因为此类对象执行的导入操作可能会影响加载模块的错误(子)解释器字典。同样重要的是,避免共享从中可以访问上述内容的对象。

还要注意,将此功能与 PyGILState_* API很微妙,因为这些API假定在Python线程状态和操作系统级线程之间有一个双射,这一假设被子解释程序打破了。强烈建议您不要在一对匹配项之间切换子解释程序。 PyGILState_Ensure()PyGILState_Release() 调用。此外,扩展(例如 ctypes )当使用子解释器时,使用这些API允许从非Python创建的线程调用Python代码可能会被破坏。

非同步通知

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

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

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

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

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

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

要在子解释器中调用此函数,调用方必须持有GIL。否则,函数 func 可以安排从错误的解释程序调用。

警告

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

在 3.9 版更改: 如果在子解释器中调用此函数,则 func 现在计划从子解释器调用,而不是从主解释器调用。每个子解释器现在都有自己的预定调用列表。

3.1 新版功能.

分析和跟踪

python解释器为附加分析和执行跟踪工具提供了一些低级支持。它们用于分析、调试和覆盖率分析工具。

这个C接口允许分析或跟踪代码以避免通过python级可调用对象调用的开销,而是直接调用C函数。该工具的基本属性没有更改;接口允许每个线程安装跟踪函数,向跟踪函数报告的基本事件与以前版本中向Python级跟踪函数报告的基本事件相同。

typedef int (*Py_tracefunc)(PyObject *obj, PyFrameObject *frame, int what, PyObject *arg)

使用注册的跟踪函数的类型 PyEval_SetProfile()PyEval_SetTrace() . 第一个参数是作为 obj框架 是事件所属的帧对象, what 是常量之一 PyTrace_CALLPyTrace_EXCEPTIONPyTrace_LINEPyTrace_RETURNPyTrace_C_CALLPyTrace_C_EXCEPTIONPyTrace_C_RETURNPyTrace_OPCODEarg 取决于 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

正在调用函数对象。

PyTrace_OPCODE

总是 Py_None .

int PyTrace_CALL

的值 what 参数 Py_tracefunc 函数:当报告对函数或方法的新调用,或生成器中的新条目时。请注意,没有报告为生成器函数创建迭代器的情况,因为在相应的框架中没有对python字节码的控制传输。

int PyTrace_EXCEPTION

的值 what 参数 Py_tracefunc 引发异常时的函数。使用此值调用回调函数 what 在处理完任何字节码之后,在执行的帧内设置异常。这样做的效果是,当异常传播导致python堆栈展开时,在异常传播时返回到每个帧时调用回调。只有跟踪函数接收这些事件;探查器不需要这些事件。

int PyTrace_LINE

作为 what 参数 Py_tracefunc 报告行号事件时的函数(但不是分析函数)。可以通过设置禁用帧 f_trace_lines0 在那个框架上。

int PyTrace_RETURN

的值 what 参数到 Py_tracefunc 当调用即将返回时的函数。

int PyTrace_C_CALL

的值 what 参数到 Py_tracefunc 当一个C函数即将被调用时。

int PyTrace_C_EXCEPTION

的值 what 参数到 Py_tracefunc 当C函数引发异常时的函数。

int PyTrace_C_RETURN

的值 what 参数到 Py_tracefunc 当C函数返回时的函数。

int PyTrace_OPCODE

的值 what 参数到 Py_tracefunc 新操作码即将执行时的函数(但不是分析函数)。默认情况下不会发出此事件:必须通过设置显式请求它 f_trace_opcodes1 在框架上。

void PyEval_SetProfile(Py_tracefunc func, PyObject *obj)

将探查器函数设置为 func . 这个 obj 参数作为第一个参数传递给函数,可以是任何python对象,或者 NULL . 如果profile函数需要保持状态,请使用不同的值 obj 为每根线提供了一个方便、安全的存放位置。配置文件函数用于除 PyTrace_LINE PyTrace_OPCODEPyTrace_EXCEPTION .

打电话的人必须按住 GIL .

void PyEval_SetTrace(Py_tracefunc func, PyObject *obj)

将跟踪函数设置为 func . 这和 PyEval_SetProfile() ,但跟踪函数不接收行号事件和每个操作码事件,但不接收与正在调用的C函数对象相关的任何事件。使用注册的任何跟踪函数 PyEval_SetTrace() 不会收到 PyTrace_C_CALLPyTrace_C_EXCEPTIONPyTrace_C_RETURN 作为 what 参数。

打电话的人必须按住 GIL .

高级调试器支持

这些功能仅用于高级调试工具。

PyInterpreterState *PyInterpreterState_Head()

返回解释器状态对象,位于所有此类对象列表的开头。

PyInterpreterState *PyInterpreterState_Main()

返回主解释器状态对象。

PyInterpreterState *PyInterpreterState_Next(PyInterpreterState *interp)

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

PyThreadState *PyInterpreterState_ThreadHead(PyInterpreterState *interp)

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

PyThreadState *PyThreadState_Next(PyThreadState *tstate)

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

线程本地存储支持

python解释器为线程本地存储(tls)提供低级支持,该线程本地存储(tls)包装了底层的本机tls实现,以支持python级线程本地存储API。 (threading.local )cpython C级API与pthreads和windows提供的API类似:使用线程键和函数关联 void* 每个线程的值。

吉尔 not 调用这些函数时需要保持;它们提供自己的锁。

注意 Python.h 不包括TLS API的声明,您需要包括 pythread.h 使用线程本地存储。

注解

这些API函数都不能代表 void* 价值观。您需要自己分配和解除分配它们。如果 void* 价值恰好是 PyObject* ,这些函数也不会对其执行refcount操作。

线程特定存储(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()

返回与用初始化的值的状态相同的值 Py_tss_NEEDS_INITNULL 在动态分配失败的情况下。

void PyThread_tss_free(Py_tss_t *key)

免费赠予 key 分配由 PyThread_tss_alloc() ,第一次调用后 PyThread_tss_delete() 以确保已取消分配任何关联的线程局部变量。如果 key 论证是 NULL .

注解

释放的键变为悬空指针,应将该键重置为 NULL .

方法

参数 key 其中的函数不能是 NULL . 而且 PyThread_tss_set()PyThread_tss_get() 如果给定的 Py_tss_t 尚未由初始化 PyThread_tss_create() .

int PyThread_tss_is_created(Py_tss_t *key)

如果给定的 Py_tss_t 已由初始化 PyThread_tss_create() .

int PyThread_tss_create(Py_tss_t *key)

成功初始化TSS密钥时返回零值。如果 key 参数不是由初始化的 Py_tss_NEEDS_INIT .这个函数可以在同一个键上重复调用——在已经初始化的键上调用它是一个no op,并立即返回success。

void PyThread_tss_delete(Py_tss_t *key)

销毁TSS密钥以忘记所有线程中与该密钥关联的值,并将该密钥的初始化状态更改为未初始化。销毁的密钥可以通过 PyThread_tss_create() . 这个函数可以在同一个键上重复调用——在已经销毁的键上调用它是一个禁忌。

int PyThread_tss_set(Py_tss_t *key, void *value)

返回零值以指示成功关联 void* 当前线程中带有TSS键的值。每个线程都有一个键到 void* 价值。

void *PyThread_tss_get(Py_tss_t *key)

返回 void* 与当前线程中的TSS键关联的值。这种回报 NULL 如果当前线程中没有与键关联的值。

线程本地存储(TLS)API

3.7 版后已移除: 此API被取代 Thread Specific Storage (TSS) API .

注解

此版本的API不支持以无法安全强制转换的方式定义本机TLS密钥的平台 int . 在这样的平台上, PyThread_create_key() 将立即返回故障状态,并且其他TLS功能将在此类平台上均为无操作。

由于上面提到的兼容性问题,不应在新代码中使用此版本的API。

int PyThread_create_key()
void PyThread_delete_key(int key)
int PyThread_set_key_value(int key, void *value)
void *PyThread_get_key_value(int key)
void PyThread_delete_key_value(int key)
void PyThread_ReInitTLS()