初始化,终结和线程

请参阅 Python 初始化配置

在Python初始化之前

在一个植入了 Python 的应用程序中,Py_Initialize() 函数必须在任何其他 Python/C API 函数之前被调用;例外的只有个别函数和 全局配置变量

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

全局配置变量

Python 有负责控制全局配置中不同特性和选项的变量。这些标志默认被 命令行选项

当一个选项设置一个旗标时,该旗标的值将是设置选项的次数。 例如,-b 会将 Py_BytesWarningFlag 设为 1 而 -bb 会将 Py_BytesWarningFlag 设为 2.

int Py_BytesWarningFlag

当将 bytesbytearraystr 比较或者将 bytesint 比较时发出警告。 如果大于等于 2 则报错。

-b 选项设置。

int Py_DebugFlag

开启解析器调试输出(限专家使用,依赖于编译选项)。

-d 选项和 PYTHONDEBUG 环境变量设置。

int Py_DontWriteBytecodeFlag

如果设置为非零, Python 不会在导入源代码时尝试写入 .pyc 文件

-B 选项和 PYTHONDONTWRITEBYTECODE 环境变量设置。

int Py_FrozenFlag

当在 Py_GetPath() 中计算模块搜索路径时屏蔽错误消息。

_freeze_importlibfrozenmain 程序使用的私有旗标。

int Py_HashRandomizationFlag

如果 PYTHONHASHSEED 环境变量被设为非空字符串则设为 1

如果该旗标为非零值,则读取 PYTHONHASHSEED 环境变量来初始化加密哈希种子。

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 将不包含脚本的目录或用户的 site-packages 目录。

-I 选项设置。

3.4 新版功能.

int Py_LegacyWindowsFSEncodingFlag

If the flag is non-zero, use the mbcs encoding instead of the UTF-8 encoding for the filesystem encoding.

如果 PYTHONLEGACYWINDOWSFSENCODING 环境变量被设为非空字符串则设为 1

更多详情请参阅 PEP 529

可用性: Windows。

int Py_LegacyWindowsStdioFlag

如果该旗标为非零值,则会使用 io.FileIO 而不是 WindowsConsoleIO 作为 sys 的标准流。

如果 PYTHONLEGACYWINDOWSSTDIO 环境变量被设为非空字符串则设为 1

有关更多详细信息,请参阅 PEP 528

可用性: Windows。

int Py_NoSiteFlag

禁用 site 的导入及其所附带的基于站点对 sys.path 的操作。 如果 site 会在稍后被显式地导入也会禁用这些操作 (如果你希望触发它们则应调用 site.main())。

-S 选项设置。

int Py_NoUserSiteDirectory

不要将 用户 site-packages 目录 添加到 sys.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 函数之前被调用;请参阅 在 Python 初始化之前 了解少数的例外情况。

这将初始化已加载模块表 (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 解释器已初始化,则返回真值(非零);否则返回假值(零)。 在调用 Py_FinalizeEx() 之后,此函数将返回假值直到 Py_Initialize() 再次被调用。

int Py_FinalizeEx()

撤销 Py_Initialize() 所做的所有初始化操作和后续对 Python/C API 函数的使用,并销毁自上次调用 Py_Initialize() 以来创建但尚未销毁的所有子解释器(参见下文 Py_NewInterpreter() 一节)。 在理想情况下,这会释放 Python 解释器分配的所有内存。 当第二次调用时(在未再次调用 Py_Initialize() 的情况下),这将不执行任何操作。 正常情况下返回值是 0。 如果在最终化(刷新缓冲数据)过程中出现错误,则返回 -1

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

Bugs and caveats: The destruction of modules and objects in modules is done in random order; this may cause destructors (__del__() methods) to fail when they depend on other objects (even functions) or modules. Dynamically loaded extension modules loaded by Python are not unloaded. Small amounts of memory allocated by the Python interpreter may not be freed (if you find a leak, please report it). Memory tied up in circular references between objects is not freed. Some memory allocated by extension modules may not be freed. Some extensions may not work properly if their initialization routine is called more than once; this can happen if an application calls Py_Initialize() and Py_FinalizeEx() more than once.

引发一个 审计事件 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 编码格式。

encoding 和/或 errors 可以为 NULL 以使用 PYTHONIOENCODING 和/或默认值(取决于其他设置)。

请注意无论是否有此设置(或任何其他设置),sys.stderr 都会使用 "backslashreplace" 错误处理句柄。

如果调用了 Py_FinalizeEx(),则需要再次调用该函数以便影响对 Py_Initialize() 的后续调用。

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

3.4 新版功能.

void Py_SetProgramName(const wchar_t *name)

如果要调用该函数,应当在首次调用 Py_Initialize() 之前调用它。 它将告诉解释器程序的 main() 函数的 argv[0] 参数的值(转换为宽字符)。 Py_GetPath() 和下面的某些其他函数会使用它在相对于解释器的位置上查找可执行文件的 Python 运行时库。 默认值是 'python'。 参数应当指向静态存储中的一个以零值结束的宽字符串,其内容在程序执行期间不会发生改变。 Python 解释器中的任何代码都不会改变该存储的内容。

Use Py_DecodeLocale() to decode a bytes string to get a wchar_* string.

wchar* Py_GetProgramName()

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

wchar_t* Py_GetPrefix()

Return the prefix for installed platform-independent files. This is derived through a number of complicated rules from the program name set with Py_SetProgramName() and some environment variables; for example, if the program name is '/usr/local/bin/python', the prefix is '/usr/local'. The returned string points into static storage; the caller should not modify its value. This corresponds to the prefix variable in the top-level Makefile and the --prefix argument to the configure script at build time. The value is available to Python code as sys.prefix. It is only useful on Unix. See also the next function.

wchar_t* Py_GetExecPrefix()

返回针对已安装的 依赖于 平台文件的 exec-prefix。 这是通过基于使用 Py_SetProgramName() 设置的程序名称和某些环境变量所派生的一系列复杂规则获得的;举例来说,如果程序名称为 '/usr/local/bin/python',则 exec-prefix 为 '/usr/local'。 返回的字符串将指向静态存储;调用方不应修改其值。 这对应于最高层级 Makefile 中的 exec_prefix 变量以及在编译时传给 configure 脚本的 --exec-prefix 参数。 该值将以 sys.exec_prefix 的名称供 Python 代码使用。 它仅适用于 Unix。

背景:当依赖于平台的文件(如可执行文件和共享库)是安装于不同的目录树中的时候 exec-prefix 将会不同于 prefix。 在典型的安装中,依赖于平台的文件可能安装于 the /usr/local/plat 子目录树而独立于平台的文件可能安装于 /usr/local

总而言之,平台是一组硬件和软件资源的组合,例如所有运行 Solaris 2.x 操作系统的 Sparc 机器会被视为相同平台,但运行 Solaris 2.x 的 Intel 机器是另一种平台,而运行 Linux 的 Intel 机器又是另一种平台。 相同操作系统的不同主要发布版通常也会构成不同的平台。 非 Unix 操作系统的情况又有所不同;这类系统上的安装策略差别巨大因此 prefix 和 exec-prefix 是没有意义的,并将被设为空字符串。 请注意已编译的 Python 字节码是独立于平台的(但并不独立于它们编译时所使用的 Python 版本!)

系统管理员知道如何配置 mountautomount 程序以在平台间共享 /usr/local 而让 /usr/local/plat 成为针对不同平台的不同文件系统。

wchar_t* Py_GetProgramFullPath()

返回 Python 可执行文件的完整程序名称;这是作为根据程序名称(由上述 Py_SetProgramName() 设置)派生默认模块搜索路径的附带影响计算得出的。 返回的字符串将指向静态存储;调用方不应修改其值。 该值将以 sys.executable 的名称供 Python 代码使用。

wchar_t* Py_GetPath()

Return the default module search path; this is computed from the program name (set by Py_SetProgramName() above) and some environment variables. The returned string consists of a series of directory names separated by a platform dependent delimiter character. The delimiter character is ':' on Unix and Mac OS X, ';' on Windows. The returned string points into static storage; the caller should not modify its value. The list sys.path is initialized with this value on interpreter startup; it can be (and usually is) modified later to change the search path for loading modules.

void Py_SetPath(const wchar_t *)

Set the default module search path. If this function is called before Py_Initialize(), then Py_GetPath() won't attempt to compute a default search path but uses the one provided instead. This is useful if Python is embedded by an application that has full knowledge of the location of all modules. The path components should be separated by the platform dependent delimiter character, which is ':' on Unix and Mac OS X, ';' on Windows.

这也将导致 sys.executable 被设为程序的完整路径 (参见 Py_GetProgramFullPath()) 而 sys.prefixsys.exec_prefix 变为空值。 如果在调用 Py_Initialize() 之后有需要则应由调用方来修改它们。

Use Py_DecodeLocale() to decode a bytes string to get a wchar_* string.

路径参数会在内部被复制,使调用方可以在调用结束后释放它。

在 3.8 版更改: 现在 sys.executable 将使用程序的完整路径,而不是程序文件名。

const char* Py_GetVersion()

返回 Python 解释器的版本。 这将为如下形式的字符串

"3.0a5+ (py3k:63103M, May 12 2008, 00:53:55) \n[GCC 4.2.3]"

The first word (up to the first space character) is the current Python version; the first three characters are the major and minor version separated by a period. The returned string points into static storage; the caller should not modify its value. The value is available to Python code as sys.version.

const char* Py_GetPlatform()

Return the platform identifier for the current platform. On Unix, this is formed from the "official" name of the operating system, converted to lower case, followed by the major revision number; e.g., for Solaris 2.x, which is also known as SunOS 5.x, the value is 'sunos5'. On Mac OS X, it is 'darwin'. On Windows, it is 'win'. The returned string points into static storage; the caller should not modify its value. The value is available to Python code as 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)

根据 argcargv 设置 sys.argv。 这些形参与传给程序的 main() 函数的类似,区别在于第一项应当指向要执行的脚本文件而不是 Python 解释器对应的可执行文件。 如果没有要运行的脚本,则 argv 中的第一项可以为空字符串。 如果此函数无法初始化 sys.argv,则将使用 Py_FatalError() 发出严重情况信号。

如果 updatepath 为零,此函数将完成操作。 如果 updatepath 为非零值,则此函数还将根据以下算法修改 sys.path:

  • 如果在 argv[0] 中传入一个现有脚本,则脚本所在目录的绝对路径将被添加到 sys.path 的开头。

  • 在其他情况下 (也就是说,如果 argc0argv[0] 未指向现有文件名),则将在 sys.path 的开头添加一个空字符串,这等价于添加当前工作目录 (".")。

Use Py_DecodeLocale() to decode a bytes string to get a wchar_* string.

注解

建议在出于执行单个脚本以外的目的嵌入 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() 设置了 updatepath1 除非 python 解释器启动时附带了 -I

Use Py_DecodeLocale() to decode a bytes string to get a wchar_* string.

在 3.4 版更改: updatepath 值依赖于 -I

void Py_SetPythonHome(const wchar_t *home)

设置默认的 "home" 目录,也就是标准 Python 库所在的位置。 请参阅 PYTHONHOME 了解该参数字符串的含义。

此参数应当指向静态存储中一个以零值结束的字符串,其内容在程序执行期间将保持不变。 Python 解释器中的代码绝不会修改此存储中的内容。

Use Py_DecodeLocale() to decode a bytes string to get a wchar_* string.

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

上面的代码块可扩展为下面的代码:

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

Note that the PyGILState_*() functions assume there is only one global interpreter (created automatically by Py_Initialize()). Python supports the creation of additional interpreters (using Py_NewInterpreter()), but mixing multiple interpreters and the PyGILState_*() API is unsupported.

有关 fork() 的注意事项

有关线程的另一个需要注意的重要问题是它们在面对 C fork() 调用时的行为。 在大多数支持 fork() 的系统中,当一个进程执行 fork 之后将只有发出 fork 的线程存在。 这对需要如何处理锁以及CPython 的运行时内所有的存储状态都会有实质性的影响。

The fact that only the "current" thread remains means any locks held by other threads will never be released. Python solves this for os.fork() by acquiring the locks it uses internally before the fork, and releasing them afterwards. In addition, it resets any 锁对象 in the child. When extending or embedding Python, there is no way to inform Python of additional (non-Python) locks that need to be acquired before or reset after a fork. OS facilities such as pthread_atfork() would need to be used to accomplish the same thing. Additionally, when extending or embedding Python, calling fork() directly rather than through os.fork() (and returning to or calling into Python) may result in a deadlock by one of Python's internal locks being held by a thread that is defunct after the fork. PyOS_AfterFork_Child() tries to reset the necessary locks, but is not always able to.

所有其他线程都将结束这一事实也意味着 CPython 的运行时状态必须妥善清理,os.fork() 就是这样做的。 这意味着最终化归属于当前解释器的所有其他 PyThreadState 对象以及所有其他 PyInterpreterState 对象。 由于这一点以及 "main" 解释器 的特殊性质,fork() 应当只在该解释器 的 "main" 线程中被调用,而 CPython 全局运行时最初就是在该线程中初始化的。 只有当 exec() 将随后立即被调用的情况是唯一的例外。

高阶 API

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

PyInterpreterState

该数据结构代表多个合作线程所共享的状态。 属于同一解释器的线程将共享其模块管理以及其他一些内部条目。 该结构体中不包含公有成员。

最初归属于不同解释器的线程不会共享任何东西,但进程状态如可用内存、打开的文件描述符等等除外。 全局解释器锁也会被所有线程共享,无论它们归属于哪个解释器。

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

Initialize and acquire the global interpreter lock. It should be called in the main thread before creating a second thread or engaging in any other thread operations such as PyEval_ReleaseThread(tstate). It is not needed before calling PyEval_SaveThread() or PyEval_RestoreThread().

This is a no-op when called for a second time.

在 3.7 版更改: 该函数现在由 Py_Initialize() 调用,因此你无需再自行调用它。

在 3.2 版更改: 此函数已不再被允许在 Py_Initialize() 之前调用。

int PyEval_ThreadsInitialized()

如果 PyEval_InitThreads() 已经被调用则返回非零值。 此函数可在不持有 GIL 的情况下被调用,因而可被用来避免在单线程运行时对加锁 API 的调用。

在 3.7 版更改: 现在 GIL 将由 Py_Initialize() 来初始化。

PyThreadState* PyEval_SaveThread()

释放全局解释器锁 (如果已创建) 并将线程状态重置为 NULL,返回之前的线程状态 (不为 NULL)。 如果锁已被创建,则当前线程必须已获取到它。

void PyEval_RestoreThread(PyThreadState *tstate)

获取全局解释器锁 (如果已创建) 并将线程状态设为 tstate,它必须不为 NULL。 如果锁已被创建,则当前线程必须尚未获取它,否则将发生死锁。

注解

当运行时正在最终化时从某个线程调用此函数将终结该线程,即使线程不是由 Python 创建的。 你可以在调用此函数之前使用 _Py_IsFinalizing()sys.is_finalizing() 来检查解释器是否还处于最终化过程中以避免不必要的终结。

PyThreadState* PyThreadState_Get()

返回当前线程状态。 全局解释器锁必须被持有。 在当前状态为 NULL 时,这将发出一个致命错误 (这样调用方将无须检查是否为 NULL)。

PyThreadState* PyThreadState_Swap(PyThreadState *tstate)

交换当前线程状态与由参数 tstate (可能为 NULL) 给出的线程状态。 全局解释器锁必须被持有且未被释放。

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

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 处于相同状态。 虽然允许递归调用,但这些句柄 不能 被共享 —— 每次对 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()

获取此线程的当前线程状态。 如果当前线程上没有使用过 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

下列所有函数都必须在 Py_Initialize() 之后被调用。

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

PyInterpreterState* PyInterpreterState_New()

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

引发一个 审计事件 cpython.PyInterpreterState_New,不附带任何参数。

void PyInterpreterState_Clear(PyInterpreterState *interp)

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

引发一个 审计事件 cpython.PyInterpreterState_Clear,不附带任何参数。

void PyInterpreterState_Delete(PyInterpreterState *interp)

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

PyThreadState* PyThreadState_New(PyInterpreterState *interp)

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

void PyThreadState_Clear(PyThreadState *tstate)

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

void PyThreadState_Delete(PyThreadState *tstate)

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

PY_INT64_T PyInterpreterState_GetID(PyInterpreterState *interp)

返回解释器的唯一 ID。 如果执行过程中发生任何错误则将返回 -1 并设置错误。

3.7 新版功能.

PyObject* PyInterpreterState_GetDict(PyInterpreterState *interp)

返回一个存储解释器专属数据的字典。 如果此函数返回 NULL 则没有任何异常被引发并且调用方应当将解释器专属字典视为不可用。

这不是 PyModule_GetState() 的替代,扩展仍应使用它来存储解释器专属的状态信息。

3.8 新版功能.

PyObject* PyThreadState_GetDict()
Return value: Borrowed reference.

返回一个扩展可以在其中存储线程专属状态信息的字典。 每个扩展都应当使用一个独有的键用来在该字典中存储状态。 在没有可用的当前线程状态时也可以调用此函数。 如果此函数返回 NULL,则还没有任何异常被引发并且调用方应当假定没有可用的当前线程状态。

int PyThreadState_SetAsyncExc(unsigned long id, PyObject *exc)

Asynchronously raise an exception in a thread. The id argument is the thread id of the target thread; exc is the exception object to be raised. This function does not steal any references to exc. To prevent naive misuse, you must write your own C extension to call this. Must be called with the GIL held. Returns the number of thread states modified; this is normally one, but will be zero if the thread id isn't found. If exc is NULL, the pending exception (if any) for the thread is cleared. This raises no exceptions.

在 3.7 版更改: The type of the id parameter changed from long to unsigned long.

void PyEval_AcquireThread(PyThreadState *tstate)

Acquire the global interpreter lock and set the current thread state to tstate, which should not be NULL. The lock must have been created earlier. If this thread already has the lock, deadlock ensues.

注解

当运行时正在最终化时从某个线程调用此函数将终结该线程,即使线程不是由 Python 创建的。 你可以在调用此函数之前使用 _Py_IsFinalizing()sys.is_finalizing() 来检查解释器是否还处于最终化过程中以避免不必要的终结。

在 3.8 版更改: 已被更新为与 PyEval_RestoreThread(), Py_END_ALLOW_THREADS()PyGILState_Ensure() 保持一致,如果在解释器正在最终化时被调用则会终结当前线程。

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

void PyEval_ReleaseThread(PyThreadState *tstate)

将当前线程状态重置为 NULL 并释放全局解释器锁。 在此之前锁必须已被创建并且必须由当前的线程所持有。 tstate 参数必须不为 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 解释器,但某些场景需要你在同一个进程甚至同一个线程中创建多个独立的解释器。 子解释器让你能够做到这一点。

“主”解释器是在运行时初始化时创建的第一个解释器。 它通常是一个进程中唯一的 Python 解释器。 与子解释器不同,主解释器具有唯一的进程全局责任比如信号处理等。 它还负责在运行时初始化期间的执行并且通常还是运行时最终化期间的活动解释器。 PyInterpreterState_Main() 函数将返回一个指向其状态的指针。

你可以使用 PyThreadState_Swap() 函数在子解释器之间进行切换。 你可以使用下列函数来创建和销毁它们:

PyThreadState* Py_NewInterpreter()

新建一个子解释器。 这是一个 (几乎) 完全隔离的 Python 代码执行环境。 特别需要注意,新的子解释器具有全部已导入模块的隔离的、独立的版本,包括基本模块 builtins, __main__sys 等。 已加载模块表 (sys.modules) 和模块搜索路径 (sys.path) 也是隔离的。 新环境没有 sys.argv 变量。 它具有新的标准 I/O 流文件对象 sys.stdin, sys.stdoutsys.stderr (不过这些对象都指向相同的底层文件描述符)。

The return value points to the first thread state created in the new sub-interpreter. This thread state is made in the current thread state. Note that no actual thread is created; see the discussion of thread states below. If creation of the new interpreter is unsuccessful, NULL is returned; no exception is set since the exception state is stored in the current thread state and there may not be a current thread state. (Like all other Python/C API functions, the global interpreter lock must be held before calling this function and is still held when it returns; however, unlike most other Python/C API functions, there needn't be a current thread state on entry.)

扩展模块将以如下方式在(子)解释器之间共享:

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

  • 对于使用单阶段初始化的模块,例如 PyModule_Create(),当特定扩展被首次导入时,它将被正常初始化,并会保存其模块字典的一个 (浅) 拷贝。 当同一扩展被另一个 (子) 解释器导入时,将初始化一个新模块并填充该拷贝的内容;扩展的 init 函数不会被调用。 因此模块字典中的对象最终会被 (子) 解释器所共享,这可能会导致预期之外的行为 (参见下文的 Bugs and caveats)。

    请注意这不同于在调用 Py_FinalizeEx()Py_Initialize() 完全重新初始化解释器之后导入扩展时所发生的情况;对于那种情况,扩展的 initmodule 函数 会被 再次调用。 与多阶段初始化一样,这意味着只有 C 层级的静态和全局变量能在这些模块之间共享。

void Py_EndInterpreter(PyThreadState *tstate)

Destroy the (sub-)interpreter represented by the given thread state. The given thread state must be the current thread state. See the discussion of thread states below. When the call returns, the current thread state is NULL. All thread states associated with this interpreter are destroyed. (The global interpreter lock must be held before calling this function and is still held when it returns.) Py_FinalizeEx() will destroy all sub-interpreters that haven't been explicitly destroyed at that point.

错误和警告

由于子解释器 (以及主解释器) 都是同一个进程的组成部分,它们之间的隔离状态并非完美 --- 举例来说,使用低层级的文件操作如 os.close() 时它们可能 (无意或恶意地) 影响它们各自打开的文件。 由于 (子) 解释器之间共享扩展的方式,某些扩展可能无法正常工作;在使用单阶段初始化或者 (静态) 全局变量时尤其如此。 在一个子解释器中创建的对象有可能被插入到另一个 (子) 解释器的命名空间中;这种情况应当尽可能地避免。

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

Also note that combining this functionality with PyGILState_*() APIs is delicate, because these APIs assume a bijection between Python thread states and OS-level threads, an assumption broken by the presence of sub-interpreters. It is highly recommended that you don't switch sub-interpreters between a pair of matching PyGILState_Ensure() and PyGILState_Release() calls. Furthermore, extensions (such as ctypes) using these APIs to allow calling of Python code from non-Python created threads will probably be broken when using sub-interpreters.

异步通知

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

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

将一个函数加入从主解释器线程调用的计划任务。 成功时,将返回 0 并将 func 加入要被主线程调用的等待队列。 失败时,将返回 -1 但不会设置任何异常。

当成功加入队列后,func最终 附带参数 arg 被主解释器线程调用。 对于正常运行的 Python 代码来说它将被异步地调用,但要同时满足以下两个条件:

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

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

警告

这是一个低层级函数,只在非常特殊的情况下有用。 不能保证 func 会尽快被调用。 如果主线程忙于执行某个系统调用,func 将不会在系统调用返回之前被调用。 此函数 通常 不适合 从任意 C 线程调用 Python 代码。 作为替代,请使用 PyGILStateAPI

3.1 新版功能.

分析和跟踪

Python 解释器为附加的性能分析和执行跟踪工具提供了一些低层级的支持。 它们可被用于性能分析、调试和覆盖分析工具。

这个 C 接口允许性能分析或跟踪代码避免调用 Python 层级的可调用对象带来的开销,它能直接执行 C 函数调用。 此工具的基本属性没有变化;这个接口允许针对每个线程安装跟踪函数,并且向跟踪函数报告的基本事件与之前版本中向 Python 层级跟踪函数报告的事件相同。

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

The type of the trace function registered using PyEval_SetProfile() and PyEval_SetTrace(). The first parameter is the object passed to the registration function as obj, frame is the frame object to which the event pertains, what is one of the constants PyTrace_CALL, PyTrace_EXCEPTION, PyTrace_LINE, PyTrace_RETURN, PyTrace_C_CALL, PyTrace_C_EXCEPTION, PyTrace_C_RETURN, or PyTrace_OPCODE, and arg depends on the value of 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

当对一个函数或方法的新调用被报告,或是向一个生成器增加新条目时传给 Py_tracefunc 函数的 what 形参的值。 请注意针对生成器函数的迭代器的创建情况不会被报告因为在相应的帧中没有向 Python字节码转移控制权。

int PyTrace_EXCEPTION

当一个异常被引发时传给 Py_tracefunc 函数的 what 形参的值。 在处理完任何字节码之后将附带 what 的值调用回调函数,在此之后该异常将会被设置在正在执行的帧中。 这样做的效果是当异常传播导致 Python 栈展开时,被调用的回调函数将随异常传播返回到每个帧。 只有跟踪函数才会接收到这些事件;性能分析器并不需要它们。

int PyTrace_LINE

The value passed as the what parameter to a Py_tracefunc function (but not a profiling function) when a line-number event is being reported. It may be disabled for a frame by setting f_trace_lines to 0 on that frame.

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 形参的值。

int PyTrace_OPCODE

The value for the what parameter to Py_tracefunc functions (but not profiling functions) when a new opcode is about to be executed. This event is not emitted by default: it must be explicitly requested by setting f_trace_opcodes to 1 on the frame.

void PyEval_SetProfile(Py_tracefunc func, PyObject *obj)

Set the profiler function to func. The obj parameter is passed to the function as its first parameter, and may be any Python object, or NULL. If the profile function needs to maintain state, using a different value for obj for each thread provides a convenient and thread-safe place to store it. The profile function is called for all monitored events except PyTrace_LINE PyTrace_OPCODE and PyTrace_EXCEPTION.

void PyEval_SetTrace(Py_tracefunc func, PyObject *obj)

Set the tracing function to func. This is similar to PyEval_SetProfile(), except the tracing function does receive line-number events and per-opcode events, but does not receive any event related to C function objects being called. Any trace function registered using PyEval_SetTrace() will not receive PyTrace_C_CALL, PyTrace_C_EXCEPTION or PyTrace_C_RETURN as a value for the what parameter.

高级调试器支持

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

PyInterpreterState* PyInterpreterState_Head()

将解释器状态对象返回到由所有此类对象组成的列表的开头。

PyInterpreterState* PyInterpreterState_Main()

返回主解释器状态对象。

PyInterpreterState* PyInterpreterState_Next(PyInterpreterState *interp)

从由解释器状态对象组成的列表中返回 interp 之后的下一项。

PyThreadState * PyInterpreterState_ThreadHead(PyInterpreterState *interp)

在由与解释器 interp 相关联的线程组成的列表中返回指向第一个 PyThreadState 对象的指针。

PyThreadState* PyThreadState_Next(PyThreadState *tstate)

从由属于同一个 PyInterpreterState 对象的线程状态对象组成的列表中返回 tstate 之后的下一项。

线程本地存储支持

The Python interpreter provides low-level support for thread-local storage (TLS) which wraps the underlying native TLS implementation to support the Python-level thread local storage API (threading.local). The CPython C level APIs are similar to those offered by pthreads and Windows: use a thread key and functions to associate a void* value per thread.

当调用这些函数时 无须 持有 GIL;它们会提供自己的锁机制。

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

注解

None of these API functions handle memory management on behalf of the void* values. You need to allocate and deallocate them yourself. If the void* values happen to be PyObject*, these functions don't do refcount operations on them either.

线程专属存储 (TSS) API

TSS API is introduced to supersede the use of the existing TLS API within the CPython interpreter. This API uses a new type Py_tss_t instead of int to represent thread keys.

3.7 新版功能.

参见

"A New C-API for Thread-Local Storage in CPython" (PEP 539)

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_INIT 初始化的值的状态相同的值,或者当动态分配失败时则返回 NULL

void PyThread_tss_free(Py_tss_t *key)

Free the given key allocated by PyThread_tss_alloc(), after first calling PyThread_tss_delete() to ensure any associated thread locals have been unassigned. This is a no-op if the key argument is NULL.

注解

A freed key becomes a dangling pointer, you should reset the key to NULL.

方法

这些函数的形参 key 不可为 NULL。 并且,如果给定的 Py_tss_t 还未被 PyThread_tss_create() 初始化则 PyThread_tss_set()PyThread_tss_get() 的行为将是未定义的。

int PyThread_tss_is_created(Py_tss_t *key)

如果给定的 Py_tss_t 已通过has been initialized by PyThread_tss_create() 被初始化则返回一个非零值。

int PyThread_tss_create(Py_tss_t *key)

当成功初始化一个 TSS 键时将返回零值。 如果 key 参数所指向的值未被 Py_tss_NEEDS_INIT 初始化则其行为是未定义的。 此函数可在相同的键上重复调用 -- 在已初始化的键上调用它将不执行任何操作并立即成功返回。

void PyThread_tss_delete(Py_tss_t *key)

销毁一个 TSS 键以便在所有线程中遗忘与该键相关联的值,并将该键的初始化状态改为未初始化的。 已销毁的键可以通过 PyThread_tss_create() 再次被初始化。 此函数可以在同一个键上重复调用 -- 但在一个已被销毁的键上调用将是无效的。

int PyThread_tss_set(Py_tss_t *key, void *value)

Return a zero value to indicate successfully associating a void* value with a TSS key in the current thread. Each thread has a distinct mapping of the key to a void* value.

void* PyThread_tss_get(Py_tss_t *key)

Return the void* value associated with a TSS key in the current thread. This returns NULL if no value is associated with the key in the current thread.

线程本地存储 (TLS) API

3.7 版后已移除: 此 API 已被 线程专属存储 (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()