python / 3.7.2rc1 / all / c-api-init.html

初始化,完成和线程

另请参见Python 初始化配置

Python 初始化之前

在嵌入 Python 的应用程序中,必须在使用任何其他 Python/C API 函数之前调用Py_Initialize()函数。除了一些Function和全局配置变量

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

全局配置变量

Python 具有用于全局配置的变量,以控制不同的Function和选项。默认情况下,这些标志由命令行选项控制。

pass选项设置标志时,标志的值是设置选项的次数。例如,-bPy_BytesWarningFlag设置为 1,-bbPy_BytesWarningFlag设置为 2.

  • Py_BytesWarningFlag

-b选项设置。

  • Py_DebugFlag
    • 打开解析器调试输出(仅针对 maven,取决于编译选项)。

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

  • Py_DontWriteBytecodeFlag
    • 如果设置为非零,Python 将不会try在导入源模块时写入.pyc文件。

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

  • Py_FrozenFlag
    • Py_GetPath()中计算模块搜索路径时,禁止显示错误消息。

_freeze_importlibfrozenmain程序使用的专用标志。

  • Py_HashRandomizationFlag
    • 如果 PYTHONHASHSEED环境变量设置为非空字符串,则设置为1

如果该标志不为零,则读取 PYTHONHASHSEED环境变量以初始化 Secret 哈希种子。

  • Py_IgnoreEnvironmentFlag

-E-I选项设置。

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

-i选项和 PYTHONINSPECT环境变量设置。

  • Py_InteractiveFlag

    • -i选项设置。
  • Py_IsolatedFlag

    • 在隔离模式下运行 Python。在隔离模式下,sys.path既不包含脚本的目录也不包含用户的 site-packages 目录。

-I选项设置。

3.4 版的新Function。

  • Py_LegacyWindowsFSEncodingFlag
    • 如果该标志不为零,则使用mbcs编码代替 UTF-8 编码作为文件系统编码。

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

有关更多详细信息,请参见 PEP 529

Availability: Windows.

  • Py_LegacyWindowsStdioFlag
    • 如果标志不为零,则对sys个标准流使用io.FileIO而不是WindowsConsoleIO

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

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

Availability: Windows.

  • Py_NoSiteFlag
    • 禁止导入模块site及其需要的与站点相关的sys.path操作。如果稍后显式导入site,也请禁用这些操作(如果希望触发它们,请调用site.main())。

-S选项设置。

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

  • Py_OptimizeFlag

  • Py_QuietFlag

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

-q选项设置。

3.2 版中的新Function。

  • Py_UnbufferedStdioFlag
    • 强制取消对 stdout 和 stderr 流的缓冲。

-u选项和 PYTHONUNBUFFERED环境变量设置。

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

-v选项和 PYTHONVERBOSE环境变量设置。

初始化和完成解释器

  • 无效Py_Initialize()
    • 初始化 Python 解释器。在嵌入 Python 的应用程序中,应在使用任何其他 Python/C API 函数之前调用此函数。有关一些 exceptions,请参见在 Python 初始化之前

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

Note

在 Windows 上,将控制台模式从O_TEXT更改为O_BINARY,这也会影响使用 C 运行时的非 Python 使用控制台。

  • 无效Py_InitializeEx(int * initsigs *)

    • 如果* initsigs 1,则此函数的作用类似于Py_Initialize()。如果 initsigs *为0,它将跳过 signal 处理程序的初始化注册,这在嵌入 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

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

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

引发不带参数的auditing event cpython._PySys_ClearAuditHooks

3.6 版的新Function。

  • 无效Py_Finalize()

Process-wide parameters

  • 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 版的新Function。

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

  • wchar_t * Py_GetProgramFullPath()
    • 返回 Python 可执行文件的完整程序名称;这是从程序名称(由上面的Py_SetProgramName()设置)派生默认模块搜索路径的副作用而计算的。返回的字符串指向静态存储;调用方不应修改其值。该值可用于sys.executable的 Python 代码。
  • wchar_t * Py_GetPath()
    • 返回默认的模块搜索路径;这是根据程序名称(由上面的Py_SetProgramName()设置)和一些环境变量计算得出的。返回的字符串由一系列目录名称组成,这些目录名称由平台相关的分隔符分隔。分隔符在 Unix 和 Mac OS X 上为':',在 Windows 上为';'。返回的字符串指向静态存储;调用方不应修改其值。列表sys.path在解释器启动时用该值初始化;以后可以(通常是)对其进行修改,以更改加载模块的搜索路径。
  • 无效Py_SetPath(const wchar_t ***)
    • 设置默认的模块搜索路径。如果在Py_Initialize()之前调用此函数,则Py_GetPath()不会try计算默认搜索路径,而是使用提供的默认搜索路径。如果 Python 是由完全了解所有模块位置的应用程序嵌入的,则这很有用。路径组件应由平台相关的分隔符分隔,在 Unix 和 Mac OS X 上为':',在 Windows 上为';'

这也将sys.executable设置为程序的完整路径(请参见Py_GetProgramFullPath()),并且sys.prefixsys.exec_prefix为空。如果需要,在调用Py_Initialize()之后,调用者可以自行修改它们。

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

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

在版本 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 版本;前三个字符是主要和次要版本,以句点分隔。返回的字符串指向静态存储;调用方不应修改其值。该值可用于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 *,wchar_t *** 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 *为0argv[0]指向现有文件名),则在sys.path之前添加一个空字符串,这与在当前工作目录("."之前)相同。

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

Note

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

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

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

版本 3.1.3 中的新Function。

  • 无效PySys_SetArgv(int * argc *,wchar_t *** argv *)
    • 除非 python 解释器以-I开头,否则此函数的工作方式类似于PySys_SetArgvEx(),而* updatepath *设置为1

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

在版本 3.4 中更改:* updatepath *值取决于-I

  • 无效Py_SetPythonHome(const wchar_t ** home *)
    • 设置默认的“主”目录,即标准 Python 库的位置。有关参数字符串的含义,请参见 PYTHONHOME

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

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

  • w_char * Py_GetPythonHome()

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

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

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

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

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

这些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。

有关 fork()的警告

关于线程的另一件重要的事情是面对 C fork()调用时线程的行为。在大多数具有fork()的系统上,在进程进行派生之后,仅存在发出该派生的线程。这对必须如何处理锁以及对 CPython 运行时中的所有存储状态都有具体影响。

仅保留“当前”线程这一事实意味着,其他线程持有的任何锁都将永远不会被释放。 Python pass在派生之前获取其内部使用的锁并在之后释放它们来解决os.fork()的问题。此外,它将重置子对象中的任何Lock Objects。在扩展或嵌入 Python 时,无法将分叉之前需要获取的其他(非 Python)锁通知 Python。需要使用诸如pthread_atfork()之类的 OS 设施来完成同一件事。此外,在扩展或嵌入 Python 时,直接调用fork()而不是passos.fork()(然后返回或调用 Python)可能会导致死锁,这是因为 Python 的内部锁之一被分叉后失效的线程持有。 PyOS_AfterFork_Child()try重置必要的锁,但并非总是能够。

所有其他线程消失的事实也意味着必须正确清理 CPython 的运行时状态,而os.fork()则需要清理。这意味着finally确定属于当前解释器的所有其他PyThreadState对象和所有其他PyInterpreterState对象。由于这个原因和"main" interpreter的特殊性质,只能在该解释器的“主”线程中调用fork(),该线程最初是在 C ++全局运行时初始化的。唯一的 exception 是exec()之后是否立即被调用。

High-level API

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

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

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

  • PyThreadState

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

    • 初始化并获取全局解释器锁。在创建第二个线程或参与任何其他线程操作(例如PyEval_ReleaseThread(tstate))之前,应在主线程中调用它。在调用PyEval_SaveThread()PyEval_RestoreThread()之前不需要它。

第二次调用时,此操作不可操作。

在 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)。如果已创建锁,则当前线程必须已获取它。
  • 无效PyEval_RestoreThread(PyThreadState ** tstate *)

    • 获取全局解释器锁(如果已创建),并将线程状态设置为* tstate *,不能为NULL。如果已创建锁,则当前线程一定不能获取它,否则将导致死锁。

Note

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

  • PyThreadState* PyThreadState_Get ( )

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

    • 将当前线程状态与参数* tstate *给定的线程状态交换,该状态可能为NULL。全局解释器锁必须保持并且不能释放。

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

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

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

Note

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

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

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

  • PyThreadState* PyGILState_GetThisThreadState ( )

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

    • 如果当前线程持有 GIL,则返回1,否则返回0。可以随时从任何线程调用此函数。仅当其 Python 线程状态已初始化且当前正在保存 GIL 时,它才会返回1。这主要是辅助Function/诊断Function。例如,在知道 GIL 已锁定可以允许调用者执行敏感操作或以其他方式表现不同时,它在回调上下文或内存分配函数中很有用。

3.4 版的新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

必须在Py_Initialize()之后调用以下所有Function。

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

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

引发不带参数的auditing event cpython.PyInterpreterState_New

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

引发不带参数的auditing event cpython.PyInterpreterState_Clear

  • 无效PyInterpreterState_Delete(PyInterpreterState ** interp *)

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

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

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

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

    • 返回解释器的唯一 ID。如果这样做有任何错误,则返回-1并设置一个错误。

3.7 版中的新Function。

  • PyObject * PyInterpreterState_GetDict(PyInterpreterState ** interp *)
    • 返回一个字典,可以在其中存储特定于解释器的数据。如果此函数返回NULL,则不会引发任何异常,并且调用者应假定没有可用的特定于解释器的 dict。

这不能替代PyModule_GetState(),extensions 应使用PyModule_GetState()来存储特定于解释器的状态信息。

3.8 版的新Function。

  • PyObject* PyThreadState_GetDict ( )
    • *返回值:借用参考。

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

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

在版本 3.7 中更改:* id *参数的类型从long更改为unsigned long

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

Note

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

在版本 3.8 中进行了更改:更新为与PyEval_RestoreThread()Py_END_ALLOW_THREADS()PyGILState_Ensure()一致,并且如果在解释程序完成时调用了当前线程,则终止当前线程。

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

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

PyEval_SaveThread()是始终可用的高层函数(即使未初始化线程)。

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

从版本 3.2 开始不推荐使用:此函数不会更新当前线程状态。请改用PyEval_RestoreThread()PyEval_AcquireThread()

Note

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

在版本 3.8 中进行了更改:更新为与PyEval_RestoreThread()Py_END_ALLOW_THREADS()PyGILState_Ensure()一致,并且如果在解释程序完成时调用了当前线程,则终止当前线程。

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

从版本 3.2 开始不推荐使用:此函数不会更新当前线程状态。请改用PyEval_SaveThread()PyEval_ReleaseThread()

Sub-interpreter support

尽管在大多数使用中,您只会嵌入一个 Python 解释器,但在某些情况下,您需要在同一过程中甚至在同一线程中创建多个独立的解释器。子解释器允许您执行此操作。

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

您可以使用PyThreadState_Swap()Function在子解释器之间切换。您可以使用以下Function创建和销毁它们:

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

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

扩展模块在(子)解释器之间共享,如下所示:

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

  • 对于使用单阶段初始化的模块,例如PyModule_Create(),是第一次导入特定 extensions 时,将正常初始化该 extensions,并将其模块字典的(浅)副本压缩掉。当另一个(子)解释程序导入相同的 extensions 时,将初始化一个新模块,并用此副本的内容填充该模块;扩展的init函数未调用。因此,模块字典中的对象finally会在(子)解释程序之间共享,这可能会导致不良行为(请参见下面的错误和警告)。

请注意,这与在pass调用Py_FinalizeEx()Py_Initialize()完全重新初始化解释器后导入 extensions 时的情况不同。在这种情况下,extensions 的initmodule函数将被再次调用。与多阶段初始化一样,这意味着这些模块之间仅共享 C 级静态变量和全局变量。

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

错误和警告

因为子解释器(和主解释器)是同一过程的一部分,所以它们之间的隔离并不是完美的-例如,使用诸如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

3.1 版中的新Function。

分析和跟踪

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

此 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_RETURNPyTrace_OPCODE,而 arg 取决于 what *的值:
什么的价值 * 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对象。
PyTrace_OPCODE Always Py_None.
  • int PyTrace_CALL

    • 当报告对函数或方法的新调用或生成器的新条目被报告时,Py_tracefunc函数的* what *参数的值。请注意,没有报告为生成器函数创建迭代器的过程,因为在相应的帧中没有控制权转移到 Python 字节码。
  • int PyTrace_EXCEPTION

    • 引发异常时,Py_tracefunc函数的* what 参数的值。在处理完任何字节码之后,将使用此值的 what *值调用回调函数,之后在执行的帧中设置异常。这样做的效果是,由于异常传播导致 Python 堆栈展开,当异常传播时,回调返回到每一帧时都会被调用。只有跟踪函数接收这些事件;探查器不需要它们。
  • int PyTrace_LINE

    • 当报告行号事件时,该值作为* what 参数传递给Py_tracefunc函数(但不是分析函数)。pass在该帧上将f_trace_lines设置为 0 *,可以禁用该帧。
  • 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

    • 即将执行新的操作码时,Py_tracefunc函数(而非概要分析函数)的* what 参数的值。默认情况下不发出此事件:必须pass将框架上的f_trace_opcodes设置为 1 *来显式请求。
  • 无效PyEval_SetProfile(Py_tracefunc * func *,PyObject ** obj *)

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

    • 将跟踪Function设置为* func 。这与PyEval_SetProfile()相似,除了跟踪函数确实接收行号事件和按操作码事件,但不接收与被调用的 C 函数对象相关的任何事件。使用PyEval_SetTrace()注册的任何跟踪函数都不会接收PyTrace_C_CALLPyTrace_C_EXCEPTIONPyTrace_C_RETURN作为 what *参数的值。

高级调试器支持

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

线程本地存储支持

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

调用这些函数时,不需要保存 GIL;他们提供自己的锁定。

请注意,Python.h不包括 TLS API 的语句,您需要包括pythread.h才能使用线程本地存储。

Note

这些 API 函数均不能代表void*值处理内存 Management。您需要自己分配和取消分配它们。如果void*值恰好是PyObject*,那么这些函数也不会对它们进行引用计数操作。

线程特定存储(TSS)API

引入了 TSS API,以取代 CPython 解释器中现有 TLS API 的使用。此 API 使用新的类型Py_tss_t而不是int来表示线程密钥。

3.7 版中的新Function。

See also

“用于 CPython 中线程本地存储的新 C-API”( PEP 539)

  • Py_tss_t
    • 该数据结构表示线程密钥的状态,其定义可能取决于基础 TLS 的实现,并且它具有一个内部字段,用于表示密钥的初始化状态。此结构中没有公共成员。

如果未定义Py_LIMITED_API,则允许Py_tss_NEEDS_INIT进行此类型的静态分配。

  • Py_tss_NEEDS_INIT

Dynamic Allocation

动态分配Py_tss_t是用Py_LIMITED_API构建的扩展模块所必需的,由于这种实现在生成时是不透明的,因此无法进行静态分配。

  • Py_tss_t* PyThread_tss_alloc ( )

    • 返回与状态Py_tss_NEEDS_INIT或动态分配失败的情况下NULL初始化的状态相同的值。
  • 无效PyThread_tss_free(Py_tss_t *)

Note

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

Methods

这些函数的参数* key *不得为NULL。此外,如果给定的Py_tss_t尚未由PyThread_tss_create()初始化,则PyThread_tss_set()PyThread_tss_get()的行为未定义。

  • int PyThread_tss_is_created(Py_tss_t *)

  • int PyThread_tss_create(Py_tss_t *)

    • 成功初始化 TSS 密钥后,返回零值。如果* key *参数所指向的值未由Py_tss_NEEDS_INIT初始化,则该行为是不确定的。可以在同一键上重复调用此函数-在已初始化的键上调用此函数是空操作,并立即返回成功。
  • 无效PyThread_tss_delete(Py_tss_t *)

    • 销毁 TSS 密钥以忘记所有线程中与该密钥关联的值,并将该密钥的初始化状态更改为未初始化。销毁的密钥可以由PyThread_tss_create()再次初始化。可以在同一键上重复调用此函数-在已销毁的键上调用此函数是无操作的。
  • int PyThread_tss_set(Py_tss_t *key ,void value *)

    • 返回零值以指示已成功将void*值与当前线程中的 TSS 密钥相关联。每个线程都具有键到void*值的独特 Map。
  • 无效* PyThread_tss_get(Py_tss_t *)

    • 返回与当前线程中的 TSS 密钥关联的void*值。如果当前线程中的键没有任何值,则返回NULL

线程本地存储(TLS)API

从 3.7 版开始不推荐使用:线程特定存储(TSS)API代替了此 API。

Note

此版本的 API 不支持无法以安全方式强制转换为int的方式定义本机 TLS 密钥的平台。在此类平台上,PyThread_create_key()将立即返回失败状态,而其他 TLS Function在此类平台上将全部变为无操作。

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

  • int PyThread_create_key()

  • 无效PyThread_delete_key(int * key *)

  • int PyThread_set_key_value(int * key *,void ** value *)

  • void * PyThread_get_key_value(int * key *)

  • 无效PyThread_delete_key_value(int * key *)

  • 无效PyThread_ReInitTLS()