Initialization, Finalization, and Threads

Initializing and finalizing the interpreter

void Py_Initialize()

初始化Python解释器。在嵌入Python的应用程序中,应该在使用任何其他Python / C API函数之前调用此函数;除了Py_SetProgramName()Py_SetPythonHome()Py_SetPath()这将初始化加载模块表(sys.modules),并创建builtins__main__sys它还初始化模块搜索路径(sys.path)。它不会设置sys.argv;使用PySys_SetArgvEx()这是第二次调用时的无操作(不首先调用Py_Finalize())。没有返回值;如果初始化失败,它是一个致命错误。

void Py_InitializeEx(int initsigs)

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

int Py_IsInitialized()

当Python解释器被初始化时返回true(非零),如果没有初始化则返回false(零)。在调用Py_Finalize()之后,返回false,直到Py_Initialize()再次被调用。

void Py_Finalize()

撤消由Py_Initialize()所做的所有初始化以及随后使用的Python / C API函数,并销毁所有的子解释器(见下面的Py_NewInterpreter()但自上一次调用Py_Initialize()以来已销毁。理想情况下,这会释放由Python解释器分配的所有内存。当第二次调用时(不首先调用Py_Initialize()),这是一个无操作。没有返回值;完成期间的错误被忽略。

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

错误和警告:模块和对象在模块中的破坏是以随机顺序完成的;这可能导致析构函数(__del__()方法)在依赖其他对象(甚至函数)或模块时失败。由Python加载的动态加载的扩展模块不会卸载。由Python解释器分配的少量内存可能不会被释放(如果您发现有泄漏,请报告)。在对象之间的循环引用中绑定的内存不会释放。扩展模块分配的某些内存可能不会释放。如果其初始化例程被多次调用,某些扩展可能无法正常工作;如果应用程序多次调用Py_Initialize()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_Finalize(),则需要再次调用此函数,以影响对Py_Initialize()的后续调用。

如果成功则返回0,如果出错则返回非零值。在解释器已经被初始化之后调用)。

版本3.4中的新功能。

void Py_SetProgramName(wchar_t *name)

在第一次调用Py_Initialize()之前,应该调用此函数,如果它被调用。它告诉解释器main()函数的argv[0]参数的值(转换为宽字符)。这由Py_GetPath()和下面的一些其他函数使用,以找到相对于解释器可执行文件的Python运行时库。默认值为'python'参数应指向静态存储中的一个零终止的宽字符串,其内容在程序执行期间不会改变。Python解释器中没有代码将更改此存储的内容。

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

wchar* Py_GetProgramName()

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

wchar_t* Py_GetPrefix()

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

wchar_t* Py_GetExecPrefix()

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

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

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

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

wchar_t* Py_GetProgramFullPath()

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

wchar_t* Py_GetPath()

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

void Py_SetPath(const wchar_t *)

设置默认模块搜索路径。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. 如果Python是由具有所有模块位置的完整知识的应用程序嵌入的,那​​么这是非常有用的。路径组件应该由平台相关的分隔符字符分隔,它在Unix上为':',在Windows上为Mac OS X,';'

这还会导致sys.executable仅设置为原始程序名称(请参阅Py_SetProgramName())和sys.prefixsys.exec_prefix为空。在调用Py_Initialize()之后,如果需要,调用者可以修改这些值。

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

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

const char* Py_GetVersion()

返回这个Python解释器的版本。这是一个看起来像的字符串

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

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

const char* Py_GetPlatform()

返回当前平台的平台标识符。在Unix上,它由操作系统的“官方”名称组成,转换为小写,后跟主要修订版本号;例如,对于Solaris 2.x(也称为SunOS 5.x),值为'sunos5'在Mac OS X上,它是'darwin'在Windows上,它是'win'返回的字符串指向静态存储;调用者不应该修改它的值。该值对于Python代码可用为sys.platform

const char* Py_GetCopyright()

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

'Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam'

返回的字符串指向静态存储;调用者不应该修改它的值。该值可用于Python代码,如sys.copyright

const char* Py_GetCompiler()

返回用于构建当前Python版本的编译器的指示,在方括号中,例如:

"[GCC 2.7.2.2]"

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

const char* Py_GetBuildInfo()

返回有关当前Python解释器实例的序列号和构建日期和时间的信息,例如

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

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

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

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

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

  • 如果现有脚本的名称在argv[0]中传递,脚本所在目录的绝对路径将预置到sys.path
  • 否则(即,如果argc为0或argv[0]不指向现有文件名),则将一个空字符串添加到sys.path,它与前面当前工作目录(".")相同。

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

注意

建议将嵌入Python解释器的应用程序用于除执行单个脚本之外的目的,将updatepath传递0,如果需要,可以更新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)

This function works like PySys_SetArgvEx() with updatepath set to 1 unless the python interpreter was started with the -I.

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

在版本3.4中更改: 更新路径值取决于-I

void Py_SetPythonHome(wchar_t *home)

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

参数应指向静态存储中的一个零终止字符串,其内容在程序执行期间不会更改。Python解释器中没有代码将更改此存储的内容。

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

w_char* Py_GetPythonHome()

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

Thread State and the Global Interpreter Lock

Python解释器不是完全线程安全的。为了支持多线程Python程序,有一个全局锁,称为global interpreter lockGIL,它必须由当前线程持有才能安全访问Python对象。没有锁,即使最简单的操作也可能导致多线程程序中的问题:例如,当两个线程同时增加同一对象的引用计数时,引用计数可能最终只增加一次而不是两次。

因此,规则存在,只有获取GIL的线程可以对Python对象操作或调用Python / C API函数。为了模拟执行的并发性,解释器定期尝试切换线程(参见sys.setswitchinterval())。该锁还围绕潜在的阻塞I / O操作(例如读取或写入文件)发布,以便其他Python线程可以同时运行。

Python解释器在一个名为PyThreadState的数据结构中保存一些特定于线程的簿记信息。还有一个全局变量指向当前的PyThreadState:它可以使用PyThreadState_Get()来检索。

Releasing the GIL from extension code

操作GIL的大多数扩展代码具有以下简单结构:

Save the thread state in a local variable.
Release the global interpreter lock.
... Do some blocking I/O operation ...
Reacquire the global interpreter lock.
Restore the thread state from the local variable.

这是常见的,存在一对宏以简化它:

Py_BEGIN_ALLOW_THREADS
... Do some blocking I/O operation ...
Py_END_ALLOW_THREADS

Py_BEGIN_ALLOW_THREADS宏打开一个新块并声明一个隐藏的局部变量; Py_END_ALLOW_THREADS宏关闭块。这两个宏仍然可用,当Python编译时没有线程支持(他们只有一个空的扩展)。

当启用线程支持时,上面的块将扩展为以下代码:

PyThreadState *_save;

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

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

注意

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

Non-Python created threads

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

如果你需要从这些线程调用Python代码(通常这将是上述第三方库提供的回调API的一部分),你必须首先通过创建线程状态数据结构来注册这些线程和解释器,然后获取GIL,最后存储他们的线程状态指针,然后才能开始使用Python / C API。当你完成后,你应该重置线程状态指针,释放GIL,最后释放线程状态数据结构。

PyGILState_Ensure()PyGILState_Release()函数自动完成上述所有操作。从C线程调用Python的典型方式是:

PyGILState_STATE gstate;
gstate = PyGILState_Ensure();

/* Perform Python actions here. */
result = CallSomeFunction();
/* evaluate result or handle exception */

/* Release the thread. No Python API allowed beyond this point. */
PyGILState_Release(gstate);

注意,PyGILState_*()函数假定只有一个全局解释器(由Py_Initialize()自动创建)。Python支持创建额外的解释器(使用Py_NewInterpreter()),但不支持混合多个解释器和PyGILState_*() API。

关于线程的另一个重要的事情是它们在C fork()调用时的行为。在大多数具有fork()的系统上,在进程仅分叉发出叉的线程将存在。这也意味着由其他线程持有的任何锁永远不会被释放。Python通过获取它在fork之前内部使用的锁,然后释放它们,为os.fork()解决这个问题。此外,它会重置子项中的任何Lock Objects当扩展或嵌入Python时,没有办法通知Python需要在fork之前获取或在fork之后重置的额外(非Python)锁。需要使用诸如pthread_atfork()之类的OS设施来完成相同的操作。此外,当扩展或嵌入Python时,直接调用fork()而不是通过os.fork()(返回或调用Python)可能导致死锁其中一个Python的内部锁由一个线程持有,该线程在fork后停止。PyOS_AfterFork()尝试重置必要的锁,但不总是能够。

High-level API

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

PyInterpreterState

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

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

PyThreadState

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

void PyEval_InitThreads()

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

这是第二次被叫时的无操作。

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

注意

当只有主线程存在时,不需要GIL操作。这是一个常见的情况(大多数Python程序不使用线程),锁操作会减慢解释器。因此,最初不会创建锁定。这种情况相当于获得了锁:当只有一个线程时,所有对象访问都是安全的。因此,当该函数初始化全局解释器锁时,它也获取它。在Python _thread模块创建新线程之前,知道它有锁或锁尚未创建,它会调用PyEval_InitThreads()当这个调用返回时,保证锁已经创建并且调用线程已经获得了它。

当未知哪个线程(如果有)当前具有全局解释器锁定时,调用此函数不是是安全的。

在编译时禁用线程支持时,此函数不可用。

int PyEval_ThreadsInitialized()

如果PyEval_InitThreads()已调用,则返回非零值。此函数可以在不持有GIL的情况下调用,因此可以用于避免在运行单线程时调用锁定API。在编译时禁用线程支持时,此函数不可用。

PyThreadState* PyEval_SaveThread()

释放全局解释器锁(如果它已被创建并且启用了线程支持)并将线程状态重置为NULL,返回上一个线程状态(不是NULL )。如果锁已经创建,当前线程必须已经获取它。(即使在编译时禁用线程支持,此功能也可用。)

void PyEval_RestoreThread(PyThreadState *tstate)

获取全局解释器锁(如果已创建并启用线程支持),并将线程状态设置为tstate,其不能为NULL如果锁已经创建,当前线程不能获取它,否则会发生死锁。(即使在编译时禁用线程支持,此功能也可用。)

PyThreadState* PyThreadState_Get()

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

PyThreadState* PyThreadState_Swap(PyThreadState *tstate)

使用参数tstate给出的线程状态交换当前线程状态,可以是NULL全局解释器锁必须保持并且不被释放。

void PyEval_ReInitThreads()

此函数从PyOS_AfterFork()调用以确保新创建的子进程不持有指向未在子进程中运行的线程的锁。

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

PyGILState_STATE PyGILState_Ensure()

确保当前线程准备好调用Python C API,而不考虑Python的当前状态或全局解释器锁。只要每个调用都与对PyGILState_Release()的调用相匹配,就可以按照线程的需要调用这么多次。通常,只要线程状态在释放之前恢复到其之前的状态,在PyGILState_Ensure()PyGILState_Release()调用之间可以使用其他线程相关的API )。例如,正常使用Py_BEGIN_ALLOW_THREADSPy_END_ALLOW_THREADS宏是可以接受的。

当调用PyGILState_Ensure()时,返回值是对线程状态的不透明“句柄”,并且必须传递到PyGILState_Release(),以确保Python保持不变州。Even though recursive calls are allowed, these handles cannot be shared - each unique call to PyGILState_Ensure() must save the handle for its call to PyGILState_Release().

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

void PyGILState_Release(PyGILState_STATE)

释放以前获得的任何资源。在这个调用之后,Python的状态将与在相应的PyGILState_Ensure()调用之前相同(但是通常这个状态对于调用者是未知的,因此使用GILState API)。

每次调用PyGILState_Ensure()都必须通过调用PyGILState_Release()来匹配。

PyThreadState* PyGILState_GetThisThreadState()

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

int PyGILState_Check()

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

版本3.4中的新功能。

通常使用以下宏,而不使用尾部分号;查找Python源代码分发中的用法。

Py_BEGIN_ALLOW_THREADS

This macro expands to { 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

所有以下函数仅在编译时启用线程支持时可用,并且必须仅在创建全局解释器锁时调用。

PyInterpreterState* PyInterpreterState_New()

创建一个新的解释器状态对象。全局解释器锁不需要被保持,但是如果有必要串行化对该函数的调用,则可以保持。

void PyInterpreterState_Clear(PyInterpreterState *interp)

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

void PyInterpreterState_Delete(PyInterpreterState *interp)

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

PyThreadState* PyThreadState_New(PyInterpreterState *interp)

创建属于给定解释器对象的新线程状态对象。全局解释器锁不需要被保持,但是如果有必要串行化对该函数的调用,则可以保持。

void PyThreadState_Clear(PyThreadState *tstate)

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

void PyThreadState_Delete(PyThreadState *tstate)

销毁线程状态对象。不需要保持全局解释器锁。线程状态必须已通过先前调用PyThreadState_Clear()重置。

PyObject* PyThreadState_GetDict()
返回值:借用引用。

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

int PyThreadState_SetAsyncExc(long id, PyObject *exc)

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

void PyEval_AcquireThread(PyThreadState *tstate)

获取全局解释器锁并将当前线程状态设置为tstate,其不应为NULL锁必须早先创建。如果这个线程已经有锁,死锁就会发生。

PyEval_RestoreThread()是一个更高级的函数,它总是可用的(即使线程支持未启用或线程尚未初始化)。

void PyEval_ReleaseThread(PyThreadState *tstate)

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

PyEval_SaveThread()是一个更高级的函数,它总是可用的(即使线程支持未启用或线程尚未初始化)。

void PyEval_AcquireLock()

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

自版本3.2后已弃用:此函数不更新当前线程状态。请改用PyEval_RestoreThread()PyEval_AcquireThread()

void PyEval_ReleaseLock()

释放全局解释器锁。锁必须早先创建。

自版本3.2后已弃用:此函数不更新当前线程状态。请改用PyEval_SaveThread()PyEval_ReleaseThread()

Sub-interpreter support

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

PyThreadState* Py_NewInterpreter()

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

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

扩展模块在(子)解释器之间共享如下:第一次导入特定扩展时,它被正常初始化,并且其模块字典的(浅)拷贝被释放。当同一个扩展由另一个(子)解释器导入时,一个新的模块被初始化并用该拷贝的内容填充;扩展的init函数不会被调用。注意,这与当通过调用Py_Finalize()Py_Initialize()完全重新初始化解释器后导入扩展时发生的情况不同;在这种情况下,扩展的initmodule函数再次调用

void Py_EndInterpreter(PyThreadState *tstate)

销毁由给定线程状态表示的(子)解释器。给定的线程状态必须是当前线程状态。请参阅下面的线程状态的讨论。当调用返回时,当前线程状态为NULL与此解释器关联的所有线程状态都被销毁。(全局解释器锁必须在调用此函数之前保持,并且在返回时仍然保持)。Py_Finalize()将销毁所有尚未在该点被显式销毁的子解释器。

Bugs and caveats

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

还要注意,将此功能与PyGILState_*() API组合是非常脆弱的,因为这些API假定在Python线程状态和操作系统级线程之间存在一个反射,这是由于存在子解释器而造成的。强烈建议您不要在一对匹配的PyGILState_Ensure()PyGILState_Release()调用之间切换子解释器。此外,使用这些API允许从非Python创建的线程调用Python代码的扩展(例如ctypes)在使用子解释器时可能会被破坏。

Asynchronous Notifications

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

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

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

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

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

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

警告

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

版本3.1中的新功能。

Profiling and Tracing

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

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

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

使用PyEval_SetProfile()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, or PyTrace_C_RETURN, and arg depends on the value of what:

什么arg的含义
PyTrace_CALL始终NULL
PyTrace_EXCEPTIONsys.exc_info()返回的异常信息。
PyTrace_LINE始终NULL
PyTrace_RETURN值返回到调用者,或NULL(如果由异常引起)。
PyTrace_C_CALL调用的函数对象。
PyTrace_C_EXCEPTION调用的函数对象。
PyTrace_C_RETURN调用的函数对象。
int PyTrace_CALL

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

int PyTrace_EXCEPTION

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

int PyTrace_LINE

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

int PyTrace_RETURN

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

int PyTrace_C_CALL

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

int PyTrace_C_EXCEPTION

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

int PyTrace_C_RETURN

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

void PyEval_SetProfile(Py_tracefunc func, PyObject *obj)

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

void PyEval_SetTrace(Py_tracefunc func, PyObject *obj)

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

PyObject* PyEval_GetCallStats(PyObject *self)

返回一个函数调用计数的元组。有为元组中的位置定义的常数:

名称
PCALL_ALL0
PCALL_FUNCTION1
PCALL_FAST_FUNCTION2
PCALL_FASTER_FUNCTION3
PCALL_METHOD4
PCALL_BOUND_METHOD5
PCALL_CFUNCTION6
PCALL_TYPE7
PCALL_GENERATOR8
PCALL_OTHER9
PCALL_POP10

PCALL_FAST_FUNCTION表示不需要创建参数元组。PCALL_FASTER_FUNCTION表示使用快速路径帧设置代码。

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

这个函数只有在编译时定义了CALL_PROFILE才有效。

Advanced Debugger Support

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

PyInterpreterState* PyInterpreterState_Head()

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

PyInterpreterState* PyInterpreterState_Next(PyInterpreterState *interp)

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

PyThreadState * PyInterpreterState_ThreadHead(PyInterpreterState *interp)

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

PyThreadState* PyThreadState_Next(PyThreadState *tstate)

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