初始化,终结和线程

在Python初始化之前

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

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

  • 配置函数:
    • PyImport_AppendInittab()
    • PyImport_ExtendInittab()
    • PyInitFrozenExtensions()
    • PyMem_SetAllocator()
    • PyMem_SetupDebugHooks()
    • PyObject_SetArenaAllocator()
    • Py_SetPath()
    • Py_SetProgramName()
    • Py_SetPythonHome()
    • Py_SetStandardStreamEncoding()
    • PySys_AddWarnOption()
    • PySys_AddXOption()
    • PySys_ResetWarnOptions()
  • 信息函数:
    • PyMem_GetAllocator()
    • PyObject_GetArenaAllocator()
    • Py_GetBuildInfo()
    • Py_GetCompiler()
    • Py_GetCopyright()
    • Py_GetPlatform()
    • Py_GetVersion()
  • 实用程序:
    • Py_DecodeLocale()
  • 内存分配器:
    • PyMem_RawMalloc()
    • PyMem_RawRealloc()
    • PyMem_RawCalloc()
    • PyMem_RawFree()

注意

Py_Initialize()全局配置变量Py_EncodeLocale(), Py_GetPath(),Py_GetPrefix(), Py_GetExecPrefix(),Py_GetProgramFullPath(), Py_GetPythonHome(),Py_GetProgramName()之前,不应调用PyEval_InitThreads().

 

以下函数

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

当一个标志由一个选项设置时,该标志的值是该选项被设置的时间数。例如,-b设置Py_BytesWarningFlag到1和-bb设置Py_BytesWarningFlag到2.

Py_BytesWarningFlag
比较bytes要么 bytearraystr要么 bytesint。如果大于等于2.

由设置-b选项。

Py_DebugFlag
打开解析器调试输出(仅限专家,取决于编译选项).

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

Py_DontWriteBytecodeFlag
如果设置为非零,Python将不会尝试写入.pyc源模块的进口文件.

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

Py_FrozenFlag
Py_GetPath().

中计算模块搜索路径时,取消错误信息_freeze_importlibfrozenmain程序设置的私有标志

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

如果标志非零,请阅读PYTHONHASHSEEDenvironmentvariable初始化秘密哈希种子.

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

设置-E-I选项

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

由设置-i选项和PYTHONINSPECTenvironmentvariable.

Py_InteractiveFlag
由设置-i选项。
Py_IsolatedFlag
以隔离模式运行Python。在隔离模式下sys.path包含脚本的目录和用户的site-packages目录.

由设置-I选项。

版本3.4.

Py_LegacyWindowsFSEncodingFlag
中的新功能如果标志非零,请使用mbcs编码而不是UTF-8编码进行文件系统编码.

设置为1如果PYTHONLEGACYWINDOWSFSENCODINGenvironmentvariable设置为非空字符串.

参见 PEP 529 了解更多详情.

可用性:Windows.

Py_LegacyWindowsStdioFlag
如果标志非零,请使用io.FileIO代替WindowsConsoleIO sys标准流.

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

参见 PEP 528了解更多详情.

可用性:Windows.

Py_NoSiteFlag
禁用导入模块site以及它所需的sys.path的站点相关操作。如果稍后显式导入site,也要禁用这些操作(调用site.main()如果你想让它们被触发).

设置-S选项.

Py_NoUserSiteDirectory
不要把user site-packages directory添加到sys.path.

-s-I选项设置,和PYTHONNOUSERSITE环境变量

Py_OptimizeFlag
设置-O选项和PYTHONOPTIMIZEenvironmentvariable.
Py_QuietFlag
即使在交互模式下也不要显示版权和版本信息.

由设置-q选项。

版本3.2.

Py_UnbufferedStdioFlag
强制stdout和stderr流无缓冲.

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

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

由设置-v选项和PYTHONVERBOSEenvironmentvariable.

初始化和完成翻译

void Py_Initialize

初始化Python解释器。在嵌入Python的应用程序中,应该在使用任何其他Python / C API函数之前调用它;看看在Python初始化之前少数例外.

初始化加载模块表(sys.modules),并创建基本模块builtins, __main__sys。它还初始化模块搜索路径(sys.path)。它没有设置sys.argv;使用PySys_SetArgvEx()。这是第二次调用时的无操作(没有先调用Py_FinalizeEx())。没有回报价值;如果初始化失败则是afatal错误.

注意

在Windows上,将控制台模式从O_TEXT更改为O_BINARY,这将影响使用C Runtime的控制台的非Python用法.

void Py_InitializeEx int  initsigs
如果Py_Initialize()initsigs,这个功能就像1一样。如果initsigs0,它会跳过信号处理程序的初始化注册,这在嵌入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解释器的应用程序可能希望释放Python卸载DLL之前分配的所有内存。在寻找内存泄漏的过程中,应用程序开发人员可能希望在退出应用程序之前释放Python分配的所有内存.

Bugs and caveats模块中模块和对象的销毁完成随机顺序;这可能会导致析构函数(__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和/或默认值(取决于在otherettings).

请注意sys.stderr总是使用“backslashreplace”错误处理程序,无论这个(或任何其他)设置如何.

如果Py_FinalizeEx()被调用时,需要调用此函数以影响后续调用Py_Initialize().

返回0如果成功,则出现错误的非零值(例如,在解释器初始化之后调用).

版本3.4.

void Py_SetProgramNameconst wchar_t  *name

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

使用Py_DecodeLocale()来解码一个字节串来得到wchar_* string.

wchar*Py_GetProgramName

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

wchar_t* Py_GetPrefix
返回prefix以获取已安装的与平台无关的文件。这是通过使用Py_SetProgramName()设置的程序名和一些环境变量的许多复杂规则得出的;例如,如果程序名称为"/usr/local/bin/python",则前缀为"/usr/local"。将字符串指向静态存储;调用者不应修改其值。这对应于顶层中的前缀Makefile变量和--prefix配置的参数脚本在构建时。该值可用于Python代码sys.prefix它只对Unix有用。另见下一个功能.
wchar_t* Py_GetExecPrefix
返回exec-prefix安装平台 – dependent文件。这是通过使用Py_SetProgramName()设置的程序名和一些环境变量的许多复杂规则得出的;例如,如果程序名称为"/usr/local/bin/python",则exec-prefix为"/usr/local"。返回的字符串指向静态存储;调用者不应修改其值。这对应于顶层中的Makefile exec_prefix --exec-prefix在构建时配置脚本的参数。该值可以作为sys.exec_prefix用于Python代码。它只适用于Unix.

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

一般而言,平台是硬件和软件系列的组合,例如,运行Solaris 2.x操作系统的Sparc机器被认为是同一平台,但运行Solaris 2.x的英特尔机器是另一个平台,运行Linux的英特尔机器是另一个平台。同一操作系统的不同主要修订版通常也形成不同的平台。非Unix操作系统是一个不同的故事;这些系统上的安装策略是如此不同,前缀和exec前缀是无意义的,并设置为空字符串。请注意,编译的Python字节码文件是独立于平台的(但不是独立于编译它们的Python版本!)。

系统管理员将知道如何配置 mount automount 程序到分享/usr/local平台之间有/usr/local/plat是每个平台的不同文件系统

wchar_t * Py_GetProgramFullPath

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

wchar_t * Py_GetPath

返回默认模块搜索路径;这是根据程序名称(由上面的Py_SetProgramName()设置)和一些环境变量计算出来的。返回的字符串由一系列由平台依赖的分隔符分隔的目录名组成。在Unix和Mac OS X上,分隔符是":",在Windows上是";"。返回的字符串指向内部存储;调用者不应修改其值。列表sys.path在解释器启动时使用此值初始化;它可以(并且通常是)稍后修改以更改loadingmodules的搜索路径.

void Py_SetPath const wchar_t  *

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

这也导致sys.executable仅设置为原始程序名(请参阅Py_SetProgramName()),并且sys.prefixsys.exec_prefix为空。在调用Py_Initialize().

后使用Py_DecodeLocale()解码一个字节字符串来获取wchar_* string.

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

const char * Py_GetVersion
返回此Python解释器的版本。这是一个看起来像

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

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

const char * Py_GetPlatform

返回当前平台的平台标识符。在Unix上,这是从操作系统的“官方”名称,转换为小写,然后是主要修订号;例如,对于Solaris 2.x,也称为SunOS 5.x,值为"sunos5"。在Mac OS X上,它是"darwin"。在Windows上,它是"win"。返回的字符串指向内部存储;调用者不应修改其值。该值可用于//代码sys.platform.

const char * Py_GetCopyright
返回当前Python版本的官方版权字符串,例如

"Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam"

返回的字符串指向静态存储;调用者不应修改其值。该值可用于//代码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  **argvint  updatepath

根据sys.argvargc设置argv。这些参数与传递给程序的参数类似main()函数与第一个条目应引用要执行的脚本文件而不是托管Python解释器的可执行文件的差异。如果没有可以运行的脚本,argv中的第一个条目可以是一个emptystring。如果此函数无法初始化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之前的版本中,你可以通过在调用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()updatepath setto 1一样,除非 python 解释器是用-I.

启动的Py_DecodeLocale()解码字节字符串以获得wchar_*字符串

更改版本3.4: updatepath值取决于-I.

void Py_SetPythonHome const wchar_t  *home
设置默认的“home”目录,即standardPython的位置库。请参阅PYTHONHOME了解theargument string的含义.

参数应指向staticstorage中以零结尾的字符串,其内容在程序执行期间不会更改。Python解释器中没有代码会改变这个存储的内容.

使用Py_DecodeLocale()解码字节字符串得到wchar_*字符串

//w_char*Py_GetPythonHome
返回默认的“home”,即前一次调用Py_SetPythonHome()设置的值,或者PYTHONHOME环境变量如果设置的话

 

Thread State和Global Interpreter Lock

Python解释器不是完全线程安全的。为了支持多线程Python程序,有一个全局锁,叫 globalinterpreter lock GIL ,必须由当前线程保存,才能安全地访问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宏打开一个新块并声明了一个ahidden局部变量;Py_END_ALLOW_THREADS宏关闭theblock.

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

PyThreadState *_save;

_save = PyEval_SaveThread();
... Do some blocking I/O operation ...
PyEval_RestoreThread(_save);

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

注意

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

 

Non-Python创建的线程

当使用专用线程创建线程时Python API(例如threading模块),一个线程状态自动与上面显示的代码相关联,因此是正确的。但是,当从C创建线程时(例如,由具有自己的线程管理的第三方库),它们不持有GIL,也没有线程状态结构.

如果你需要从这些线程调用Python代码(通常这将是上述第三方库提供的回调API的一部分),你必须首先通过创建线程状态数据结构,然后获取GIL来向解释器注册这些线程,在开始使用Python / CAPI之前,最后存储它们的线程状态指针。完成后,你应该重置线程状态指针,释放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_*()函数假设只有一个globalinterpreter(由Py_Initialize()自动创建)。Pythons支持创建额外的解释器(使用Py_NewInterpreter()),但混合多个解释器和PyGILState_*()API不受支持

关于线程的另一个重要注意事项是它们在C fork()调用中的行为。在大多数系统上fork(),在进程分叉之后,只存在发出fork的线程。alsomeans其他线程持有的任何锁永远不会被释放。通过获取它在fork之前内部使用的锁,并在之后释放它们来解决os.fork()的Python问题。另外,它会重置锁定物体在孩子身上。在扩展或嵌入Python时,没有办法告知Python需要在fork之前获取或在fork之后重置的其他(非Python)锁。需要使用pthread_atfork()等操作系统来完成相同的操作。另外,在扩展或嵌入Python时,直接调用fork()而不是通过os.fork()(并返回到或者调用Python)可能会导致一个Python的内部锁定所造成的死锁,这个锁定是在fork.PyOS_AfterFork_Child()尝试重置必要的锁之后解散的一个线程所持有的,但是总是无法解决.

高级API

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

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

属于不同解释器的线程最初没有任何共享,除了可用内存,打开文件描述符等进程状态。globalinterpreter lock也由所有线程共享,无论它们属于哪个解释器.

PyThreadState
这个数据结构代表单个线程的状态。唯一的publicdata成员是PyInterpreterState *interp,它指向该线程的解释器状态.
void PyEval_InitThreads

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

这是第二次调用时的无操作.

在版本3.7中更改:此功能现在由Py_Initialize()调用,所以你不必打电话它自己了.

在版本3.2:Py_Initialize()anymore.

 

int PyEval_ThreadsInitialized
如果PyEval_InitThreads()被称为。可以在不保持GIL的情况下调用此函数,因此可以在运行single -threaded时使用它来禁止对锁定API的调用.

更改版本3.7:GIL 现在由Py_Initialize().

初始化PyThreadState * PyEval_SaveThread
释放全局解释器锁(如果已创建)和threadsupport已启用)并将线程状态重置为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_Child()调用到确保新创建的子进程不保存指向未在子进程中运行的线程的锁.

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

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()必须保存其callto的句柄PyGILState_Release().

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

void PyGILState_Release PyGILState_STATE
释放以前获得的任何资源。在此调用之后,Python的状态将与相应的PyGILState_Ensure()调用之前的状态相同(但通常调用者将不知道此状态,因此使用了GILState API).

每个调用PyGILState_Ensure()必须在同一个线程上调用PyGILState_Release()来匹配

PyThreadState* PyGILState_GetThisThreadState
获取此线程的当前线程状态。如果在当前线程上使用了noGILState API,则可以返回NULL。请注意,即使主线程上没有进行自动线程状态调用,主线程也具有这样的线程状态。这主要是辅助/诊断功能.
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
创建一个新的解释器状态对象。不需要保持全局解释器锁,但是如果需要序列化对该函数的调用,则可以保持它.
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().
PY_INT64_T PyInterpreterState_GetID PyInterpreterState  *interp
返回解释器的唯一ID。如果在执行中有任何错误,则返回-1并设置错误.

版本3.7.

PyObject* PyThreadState_GetDict
Return value: Borrowed reference.

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

int PyThreadState_SetAsyncExc unsigned long  id,PyObject  *exc
在线程中异步引发异常。idargument是目标线程的threadid;exc是要引发的异常对象。这个函数不会窃取对exc的任何引用。为了防止天真滥用,你必须编写自己的C扩展来调用它。必须在保持GIL的情况下调用。返回已修改的线程状态数;这通常是一个,但如果找不到线程id,则为零。如果excNULL,则清除线程的pendingexception(如果有的话)。这没有例外.

版本3.7: id参数的类型从long改为unsigned long.

void PyEval_AcquireThread PyThreadState  *tstate
获取全局解释器锁并将当前线程状态设置为tstate,这不应该是NULL。必须先创建锁。如果此线程已经锁定,则会发生死锁.

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

void PyEval_ReleaseThread PyThreadState  *tstate
将当前线程状态重置为NULL并释放全局解释器锁。锁必须先创建,并且必须由currentthread保存。tstate参数,不能是NULL,仅用于检查它是否代表当前线程状态 – 如果不是,则报告致命错误.

PyEval_SaveThread()是一个总是可用的高级函数(即使线程尚未初始化).

void PyEval_AcquireLock
获取全局解释器锁。必须先创建锁。如果此线程已经锁定,则会出现死锁.

自版本3.2:后重新编写此函数不会更新当前线程状态。请使用PyEval_RestoreThread()PyEval_AcquireThread()instead.

void PyEval_ReleaseLock
释放全局解释器锁。锁必须是先前创建的.

自版本3.2:后重新编写此函数不会更新当前的线程状态。请使用PyEval_SaveThread()PyEval_ReleaseThread()instead.

 

子解释支持

虽然在大多数用途中,您只会嵌入一个Python解释器,但在这种情况下,您需要在同一进程中创建几个独立的解释器,甚至可能在同一个线程中。副译员允许你这样做。您可以使用PyThreadState_Swap()功能在子解释器之间切换。您可以使用以下函数创建和销毁它们:

PyThreadState * Py_NewInterpreter

创建一个新的子解释器。这是一个(几乎)完全独立的环境,用于执行Python代码。特别是,新的解释器具有所有导入模块的独立版本,包括基本模块builtins, __main__sys。加载模块的表(sys.modules)和模块搜索路径(sys.path)也是分开的。新环境没有sys.argv变量。它有新的标准I / O流文件对象sys.stdin,sys.stdoutsys.stderr(但是它们指的是相同的底层文件描述符).

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

扩展模块在(子)解释器之间共享如下:第一次导入特定扩展,正常初始化,并且其模块字典的(浅)副本被squirreled。当另一个(子)解释器导入相同的扩展时,初始化一个新模块并填充该副本的内容;扩展名的init函数未被调用。请注意,这与通过调用Py_FinalizeEx()Py_Initialize()完全重新初始化解释器后导入扩展名时的情况不同。在这种情况下,扩展名的initmodule功能is再次调用.

 

void Py_EndInterpreter PyThreadState  *tstate

销毁由给定线程状态表示的(子)解释器。giventhread状态必须是当前线程状态。请参阅下面的threadstates讨论。当调用返回时,当前线程状态为NULL。与此解释器关联的Allthread状态将被销毁。(必须在调用此函数之前保持globalinterpreter锁定,并且在返回时仍然保持。)Py_FinalizeEx()将销毁那时尚未明确销毁的所有子解释器.

Bugs and caveats

因为子解释器(和主解释器)是同一个进程的一部分,它们之间的绝缘并不完美 – 例如,使用像os.close()他们可以(意外或恶意)影响彼此的打开文件。由于(子)解释器之间共享扩展,一些扩展可能无法正常工作;当扩展使用(静态)全局变量时,或者扩展在初始化后操作其模块的字典时,这种情况尤其可能发生。可以将在一个子解释器中创建的对象插入到另一个子解释器的命名空间中;这应该非常小心,以避免在子解释器之间共享用户定义的函数,方法,实例或类,因为这些对象执行的导入操作可能会影响错误的(子)解释器的loadedmodules字典。

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

异步通知

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

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

安排从主解释器线程调用的函数。Onsuccess,0返回func排队等待在主线程中调用。失败时,-1返回时没有设置任何异常.

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

  • 字节码边界;
  • 主线程持有全局解释器锁func因此可以使用完整的C API).

func必须在成功时返回0,或-1因异常而失败。func不会被中断以递归方式执行另一个异步通知,但如果释放全局解释器锁,它仍然可以被中断到switchthreads .

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

Warning

这是一个低级功能,仅适用于非常特殊的情况。不能保证func会尽可能快地调用。如果主线程忙于执行系统调用,func在系统调用返回之前不会被调用。这个函数一般适合调用Python代码fromarbitrary C线程。相反,使用 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_CALL,PyTrace_EXCEPTION, PyTrace_LINE, PyTrace_RETURN,PyTrace_C_CALL, PyTrace_C_EXCEPTION, PyTrace_C_RETURNPyTrace_OPCODE,和arg取决于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当报告函数或方法的newcall或生成器的新条目时函数。注意,没有报告为生成器函数创建迭代器,因为没有控制转移到相应框架中的Python字节码。
int PyTrace_EXCEPTION
当引发异常时,what参数的值为Py_tracefunc函数。使用what在处理任何字节码之后,在正在执行的帧内设置异常。这样做的结果是,由于异常传播导致Python堆栈展开,因此当异常传播时,回调被调用到每个帧。只有跟踪功能才能接收这些事件;分析器不需要它们
// PyTrace_LINE
what参数传递给Py_tracefunc函数(但不是分析函数)的值时正在报告行号事件。可以通过在该帧上设置f_trace_lines0来禁用帧.
int PyTrace_RETURN
值对于what参数到Py_tracefunc当acall即将返回时的函数.
int PyTrace_C_CALL
what参数的值为Py_tracefunc即将调用Cfunction时的函数
// PyTrace_C_EXCEPTION
//功能时what参数的值Py_tracefunc起作用已经引发异常.
int PyTrace_C_RETURN
当一个Cfunction返回时,what参数的值为Py_tracefunc的功能.
// PyTrace_OPCODE
的值调用回调函数what当一个新的操作码即将被执行时,Py_tracefunc函数的参数(但不是配置函数)。默认情况下不会发出此事件:必须通过将f_trace_opcodes设置为1在框架上
void PyEval_SetProfile Py_tracefunc  func,PyObject  *obj
设置探查器功能至 funcobj参数作为第一个参数传递给函数,可以是任何Python对象,或NULL。如果配置文件功能需要维护状态,请使用不同的值obj为每个线程提供了一个方便且线程安全的存储位置。除了PyTrace_LINEPyTrace_OPCODEPyTrace_EXCEPTION.
无效PyEval_SetTracePy_tracefunc  func,PyObject  *obj
将跟踪功能设置为func。这类似于PyEval_SetProfile(),除了跟踪函数确实接收line-numberevents和per-opcode事件,但是没有收到与被调用的C函数对象相关的任何事件。使用PyEval_SetTrace()注册的任何跟踪功能都不会接收PyTrace_C_CALL, PyTrace_C_EXCEPTIONPyTrace_C_RETURN作为what参数的值

 

Advanced Debugger Support

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

PyInterpreterState * PyInterpreterState_Head
返回解释器状态对象在所有这些对象的列表的头部.
PyInterpreterState* PyInterpreterState_Next PyInterpreterState  *interp
返回下一个从所有对象的列表interp之后解释状态对象.
PyThreadState * PyInterpreterState_ThreadHead PyInterpreterState  *interp
将指针返回到与解释器关联的线程列表中的第一个PyThreadState对象interp.
PyThreadState * PyThreadState_Next PyThreadState  *tstate
从属于同一个tstate的所有这些对象的列表中返回PyInterpreterStateobject

 

//本地存储支持

Python解释器为线程本地存储(TLS)提供低级支持,它包装底层本机TLS实现以支持Python级线程本地存储API(threading.local)。CPython C级API类似于pthreads和Windows提供的API:使用线程键和函数关联void*值perthread.

GIL确实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

Py_tss_t
此数据结构表示线程键的状态,其定义为可能依赖于底层的TLS实现,它有一个表示密钥初始化状态的内部字段。在这个结构中有nopublic成员.

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
释放给定的keyPyThread_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_createdPy_tss_t  *key
如果给定Py_tss_t已被PyThread_tss_create().
int PyThread_tss_createPy_tss_t  *key
成功初始化TSS密钥时返回零值。如果key参数指向的值未被Py_tss_NEEDS_INIT初始化,则行为未定义。这个函数可以在同一个键上重复调用 – 在已经初始化的键上调用它是ano-op并立即返回成功.
void PyThread_tss_delete Py_tss_t  *key
销毁TSS密钥以忘记跨所有线程的密钥关联值,并将密钥的初始化状态更改为未初始化。Adestroyed键可以通过PyThread_tss_create()再次初始化。这个函数可以在同一个键上重复调用 – 在已经被破坏的键上调用它是一个no-op.
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被取代线程特定存储(TSS)API .

注意

此版本的API不支持以无法安全地转换为int的方式定义本机TLS键的平台。在这样的平台上,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_valueint  key
void PyThread_ReInitTLS