初始化,最终化和线程¶
请参阅 Python 初始化配置 。
在Python初始化之前¶
在一个植入了 Python 的应用程序中,Py_Initialize()
函数必须在任何其他 Python/C API 函数之前被调用;例外的只有个别函数和 全局配置变量。
在初始化Python之前,可以安全地调用以下函数:
配置函数:
信息函数:
工具
内存分配器:
全局配置变量¶
Python 有负责控制全局配置中不同特性和选项的变量。这些标志默认被 命令行选项。
当一个选项设置一个旗标时,该旗标的值将是设置选项的次数。 例如,-b
会将 Py_BytesWarningFlag
设为 1 而 -bb
会将 Py_BytesWarningFlag
设为 2.
-
int Py_BytesWarningFlag¶
此 API 仅为向下兼容而保留:应当改为设置
PyConfig.bytes_warning
,参见 Python 初始化配置。当将
bytes
或bytearray
与str
比较或者将bytes
与int
比较时发出警告。 如果大于等于2
则报错。由
-b
选项设置。自 3.12 版本弃用.
-
int Py_DebugFlag¶
此 API 仅为向下兼容而保留:应当改为设置
PyConfig.parser_debug
,参见 Python 初始化配置。开启解析器调试输出(限专家使用,依赖于编译选项)。
由
-d
选项和PYTHONDEBUG
环境变量设置。自 3.12 版本弃用.
-
int Py_DontWriteBytecodeFlag¶
此 API 仅为向下兼容而保留:应当改为设置
PyConfig.write_bytecode
,参见 Python 初始化配置。如果设置为非零, Python 不会在导入源代码时尝试写入
.pyc
文件由
-B
选项和PYTHONDONTWRITEBYTECODE
环境变量设置。自 3.12 版本弃用.
-
int Py_FrozenFlag¶
此 API 仅为向下兼容而保留:应当改为设置
PyConfig.pathconfig_warnings
,参见 Python 初始化配置。当在
Py_GetPath()
中计算模块搜索路径时屏蔽错误消息。由
_freeze_importlib
和frozenmain
程序使用的私有旗标。自 3.12 版本弃用.
-
int Py_HashRandomizationFlag¶
此 API 仅为向下兼容而保留:应当改为设置
PyConfig.hash_seed
和PyConfig.use_hash_seed
,参见 Python 初始化配置。如果
PYTHONHASHSEED
环境变量被设为非空字符串则设为1
。如果该旗标为非零值,则读取
PYTHONHASHSEED
环境变量来初始化加密哈希种子。自 3.12 版本弃用.
-
int Py_IgnoreEnvironmentFlag¶
此 API 仅为向下兼容而保留:应当改为设置
PyConfig.use_environment
,参见 Python 初始化配置。忽略所有
PYTHON*
环境变量,例如可能设置的PYTHONPATH
和PYTHONHOME
。自 3.12 版本弃用.
-
int Py_InspectFlag¶
此 API 被保留用于向下兼容:应当改为采用设置
PyConfig.inspect
,参见 Python 初始化配置。当将脚本作为第一个参数传入或是使用了
-c
选项时,则会在执行该脚本或命令后进入交互模式,即使在sys.stdin
并非一个终端时也是如此。由
-i
选项和PYTHONINSPECT
环境变量设置。自 3.12 版本弃用.
-
int Py_InteractiveFlag¶
此 API 被保留用于向下兼容:应当改为采用设置
PyConfig.interactive
,参见 Python 初始化配置。由
-i
选项设置。自 3.12 版本弃用.
-
int Py_IsolatedFlag¶
此 API 被保留用于向下兼容:应当改为设置
PyConfig.isolated
,参见 Python 初始化配置。以隔离模式运行 Python. 在隔离模式下
sys.path
将不包含脚本的目录或用户的 site-packages 目录。由
-I
选项设置。Added in version 3.4.
自 3.12 版本弃用.
-
int Py_LegacyWindowsFSEncodingFlag¶
此 API 被保留用于向下兼容:应当改为设置
PyPreConfig.legacy_windows_fs_encoding
,参见 Python 初始化配置。如果该旗标为非零值,则使用
mbcs
编码和``replace`` 错误处理器,而不是 UTF-8 编码和surrogatepass
错误处理器作用 filesystem encoding and error handler。如果
PYTHONLEGACYWINDOWSFSENCODING
环境变量被设为非空字符串则设为1
。更多详情请参阅 PEP 529。
Availability: Windows.
自 3.12 版本弃用.
-
int Py_LegacyWindowsStdioFlag¶
此 API 被保留用于向下兼容:应当改为设置
PyConfig.legacy_windows_stdio
,参见 Python 初始化配置。如果该旗标为非零值,则会使用
io.FileIO
而不是io._WindowsConsoleIO
作为sys
标准流。如果
PYTHONLEGACYWINDOWSSTDIO
环境变量被设为非空字符串则设为1
。有关更多详细信息,请参阅 PEP 528。
Availability: Windows.
自 3.12 版本弃用.
-
int Py_NoSiteFlag¶
此 API 被保留用于向下兼容:应当改为设置
PyConfig.site_import
,参见 Python 初始化配置。禁用
site
的导入及其所附带的基于站点对sys.path
的操作。 如果site
会在稍后被显式地导入也会禁用这些操作 (如果你希望触发它们则应调用site.main()
)。由
-S
选项设置。自 3.12 版本弃用.
-
int Py_NoUserSiteDirectory¶
此 API 被保留用于向下兼容:应当改为设置
PyConfig.user_site_directory
,参见 Python 初始化配置。不要将
用户 site-packages 目录
添加到sys.path
。由
-s
和-I
选项以及PYTHONNOUSERSITE
环境变量设置。自 3.12 版本弃用.
-
int Py_OptimizeFlag¶
此 API 被保留用于向下兼容:应当改为
PyConfig.optimization_level
,参见 Python 初始化配置。由
-O
选项和PYTHONOPTIMIZE
环境变量设置。自 3.12 版本弃用.
-
int Py_QuietFlag¶
此 API 被保留用于向下兼容:应当改为设置
PyConfig.quiet
,参见 Python 初始化配置。即使在交互模式下也不显示版权和版本信息。
由
-q
选项设置。Added in version 3.2.
自 3.12 版本弃用.
-
int Py_UnbufferedStdioFlag¶
此 API 被保留用于向下兼容:应当改为设置
PyConfig.buffered_stdio
,参见 Python 初始化配置。强制 stdout 和 stderr 流不带缓冲。
由
-u
选项和PYTHONUNBUFFERED
环境变量设置。自 3.12 版本弃用.
-
int Py_VerboseFlag¶
此 API 被保留用于向下兼容:应当改为设置
PyConfig.verbose
,参见 Python 初始化配置。每次初始化模块时打印一条消息,显示加载模块的位置(文件名或内置模块)。 如果大于或等于
2
,则为搜索模块时检查的每个文件打印一条消息。 此外还会在退出时提供模块清理信息。由
-v
选项和PYTHONVERBOSE
环境变量设置。自 3.12 版本弃用.
初始化和最终化解释器¶
-
void Py_Initialize()¶
- 属于 稳定 ABI.
初始化 Python 解释器。 在嵌入 Python 的应用程序中,它应当在使用任何其他 Python/C API 函数之前被调用;请参阅 在 Python 初始化之前 了解少数的例外情况。
这将初始化已加载模块表 (
sys.modules
),并创建基本模块builtins
、__main__
和sys
。 它还会初始化模块搜索路径 (sys.path
)。 它不会设置sys.argv
;如有需要请使用PySys_SetArgvEx()
。 当第二次调用时 (在未事先调用Py_FinalizeEx()
的情况下) 将不会执行任何操作。 它没有返回值;如果初始化失败则会发生致命错误。使用
Py_InitializeFromConfig()
函数自定义 Python 初始化配置。备注
在 Windows 上,将控制台模式从
O_TEXT
改为O_BINARY
,这还将影响使用 C 运行时的非 Python 的控制台使用。
-
void Py_InitializeEx(int initsigs)¶
- 属于 稳定 ABI.
如果 initsigs 为
1
则该函数的工作方式与Py_Initialize()
类似。 如果 initsigs 为0
,它将跳过信号处理器的初始化注册,这在嵌入 Python 时可能会很有用处。使用
Py_InitializeFromConfig()
函数自定义 Python 初始化配置。
-
int Py_IsInitialized()¶
- 属于 稳定 ABI.
如果 Python 解释器已初始化,则返回真值(非零);否则返回假值(零)。 在调用
Py_FinalizeEx()
之后,此函数将返回假值直到Py_Initialize()
再次被调用。
-
int Py_FinalizeEx()¶
- 属于 稳定 ABI 自 3.6 版起.
撤销
Py_Initialize()
所做的所有初始化和随后对 Python/C API 函数的使用,并销毁自上次调用Py_Initialize()
以来创建但尚未销毁的所有子解释器 (见下文Py_NewInterpreter()
)。 在理想情况下,这会释放 Python 解释器分配的所有内存。 当第二次调用时(在没有再次调用Py_Initialize()
的情况下),该函数不执行任何操作。由于这是
Py_Initialize()
的逆向操作,因而它应当在激活同一解释器的同一线程中被调用。 这意味着主线程和主解释器。 当Py_RunMain()
仍然运行时则绝不应调用此函数。通常返回值为
0
。 如果在最终化(刷新缓冲的数据)期间发生错误,则返回-1
。提供此函数的原因有很多。嵌入应用程序可能希望重新启动Python,而不必重新启动应用程序本身。从动态可加载库(或DLL)加载Python解释器的应用程序可能希望在卸载DLL之前释放Python分配的所有内存。在搜索应用程序内存泄漏的过程中,开发人员可能希望在退出应用程序之前释放Python分配的所有内存。
程序问题和注意事项: 模块和模块中对象的销毁是按随机顺序进行的;这可能导致依赖于其他对象(甚至函数)或模块的析构器(即
__del__()
方法)出错。 Python 所加载的动态加载扩展模块不会被卸载。 Python 解释器所分配的少量内存可能不会被释放(如果发现内存泄漏,请报告问题)。 对象间循环引用所占用的内存不会被释放。 扩展模块所分配的某些内存可能不会被释放。 如果某些扩展的初始化例程被调用多次它们可能无法正常工作;如果应用程序多次调用了Py_Initialize()
和Py_FinalizeEx()
就可能发生这种情况。引发一个不带参数的 审计事件
cpython._PySys_ClearAuditHooks
。Added in version 3.6.
-
void Py_Finalize()¶
- 属于 稳定 ABI.
这是一个不考虑返回值的
Py_FinalizeEx()
的向下兼容版本。
进程级参数¶
-
int Py_SetStandardStreamEncoding(const char *encoding, const char *errors)¶
此 API 被保留用于向下兼容:应当改为设置
PyConfig.stdio_encoding
和PyConfig.stdio_errors
,参见 Python 初始化配置。如果要调用该函数,应当在
Py_Initialize()
之前调用。 它指定了标准 IO 使用的编码格式和错误处理方式,其含义与str.encode()
中的相同。它覆盖了
PYTHONIOENCODING
的值,并允许嵌入代码以便在环境变量不起作用时控制 IO 编码格式。encoding 和/或 errors 可以为
NULL
以使用PYTHONIOENCODING
和/或默认值(取决于其他设置)。请注意无论是否有此设置(或任何其他设置),
sys.stderr
都会使用 "backslashreplace" 错误处理器。如果调用了
Py_FinalizeEx()
,则需要再次调用该函数以便影响对Py_Initialize()
的后续调用。成功时返回
0
,出错时返回非零值(例如在解释器已被初始化后再调用)。Added in version 3.4.
自 3.11 版本弃用.
-
void Py_SetProgramName(const wchar_t *name)¶
- 属于 稳定 ABI.
此 API 被保留用于向下兼容:应当改为设置
PyConfig.program_name
,参见 Python 初始化配置。如果要调用该函数,应当在首次调用
Py_Initialize()
之前调用它。 它将告诉解释器程序的main()
函数的argv[0]
参数的值(转换为宽字符)。Py_GetPath()
和下面的某些其他函数会使用它在相对于解释器的位置上查找可执行文件的 Python 运行时库。 默认值是'python'
。 参数应当指向静态存储中的一个以零值结束的宽字符串,其内容在程序执行期间不会发生改变。 Python 解释器中的任何代码都不会改变该存储的内容。使用
Py_DecodeLocale()
对字节串进行解码以得到一个 wchar_t* 字符串。自 3.11 版本弃用.
-
wchar_t *Py_GetProgramName()¶
- 属于 稳定 ABI.
返回用
Py_SetProgramName()
设置的程序名称,或默认的名称。 返回的字符串指向静态存储;调用者不应修改其值。此函数不应在
Py_Initialize()
之前被调用,否则将返回NULL
。在 3.10 版本发生变更: 现在如果它在
Py_Initialize()
之前被调用将返回NULL
。
-
wchar_t *Py_GetPrefix()¶
- 属于 稳定 ABI.
返回针对已安装的独立于平台文件的 prefix。 这是通过基于使用
Py_SetProgramName()
设置的程序名称和某些环境变量所派生的一系列复杂规则获得的;举例来说,如果程序名称为'/usr/local/bin/python'
,则 prefix 为'/usr/local'
。 返回的字符串将指向静态存储;调用方不应修改其值。 这对应于最高层级Makefile
中的 prefix 变量以及在编译时传给 configure 脚本的--prefix
参数。 该值将以sys.prefix
的名称供 Python 代码使用。 它仅适用于 Unix。 另请参见下一个函数。此函数不应在
Py_Initialize()
之前被调用,否则将返回NULL
。在 3.10 版本发生变更: 现在如果它在
Py_Initialize()
之前被调用将返回NULL
。
-
wchar_t *Py_GetExecPrefix()¶
- 属于 稳定 ABI.
返回针对已安装的 依赖于 平台文件的 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 版本!)
系统管理员知道如何配置 mount 或 automount 程序以在平台间共享
/usr/local
而让/usr/local/plat
成为针对不同平台的不同文件系统。此函数不应在
Py_Initialize()
之前被调用,否则将返回NULL
。在 3.10 版本发生变更: 现在如果它在
Py_Initialize()
之前被调用将返回NULL
。
-
wchar_t *Py_GetProgramFullPath()¶
- 属于 稳定 ABI.
返回 Python 可执行文件的完整程序名称;这是作为根据程序名称(由上述
Py_SetProgramName()
设置)派生默认模块搜索路径的附带影响计算得出的。 返回的字符串将指向静态存储;调用方不应修改其值。 该值将以sys.executable
的名称供 Python 代码使用。此函数不应在
Py_Initialize()
之前被调用,否则将返回NULL
。在 3.10 版本发生变更: 现在如果它在
Py_Initialize()
之前被调用将返回NULL
。
-
wchar_t *Py_GetPath()¶
- 属于 稳定 ABI.
返回默认模块搜索路径;这是根据程序名称(由上述
Py_SetProgramName()
设置)和某些环境变量计算得出的。 返回的字符串由一系列由依赖于平台的分隔符分开的目录名称组成。 分隔符在 Unix 和 macOS 上为':'
而在 Windows 上为';'
。 返回的字符串将指向静态存储;调用方不应修改其值。 列表sys.path
将在解释器启动时使用该值来初始化;它可以在随后被修改(并且通常都会被修改)以变更加载模块的搜索路径。此函数不应在
Py_Initialize()
之前被调用,否则将返回NULL
。在 3.10 版本发生变更: 现在如果它在
Py_Initialize()
之前被调用将返回NULL
。
-
void Py_SetPath(const wchar_t*)¶
- 属于 稳定 ABI 自 3.7 版起.
此 API 被保留用于向下兼容:应当改为采用设置
PyConfig.module_search_paths
和PyConfig.module_search_paths_set
,参见 Python 初始化配置。设置默认的模块搜索路径。 如果此函数在
Py_Initialize()
之前被调用,则Py_GetPath()
将不会尝试计算默认的搜索路径而是改用已提供的路径。 这适用于由一个完全知晓所有模块的位置的应用程序来嵌入 Python 的情况。 路径组件应当由平台专属的分隔符来分隔,在 Unix 和 macOS 上是':'
而在 Windows 上则是';'
。这也将导致
sys.executable
被设为程序的完整路径 (参见Py_GetProgramFullPath()
) 而sys.prefix
和sys.exec_prefix
变为空值。 如果在调用Py_Initialize()
之后有需要则应由调用方来修改它们。使用
Py_DecodeLocale()
来解码字节串以得到一个 wchar_* 字符串。路径参数会在内部被复制,使调用方可以在调用结束后释放它。
在 3.8 版本发生变更: 现在
sys.executable
将使用程序的完整路径,而不是程序文件名。自 3.11 版本弃用.
-
const char *Py_GetVersion()¶
- 属于 稳定 ABI.
返回 Python 解释器的版本。 这将为如下形式的字符串
"3.0a5+ (py3k:63103M, May 12 2008, 00:53:55) \n[GCC 4.2.3]"
第一个单词(到第一个空格符为止)是当前的 Python 版本;前面的字符是以点号分隔的主要和次要版本号。 返回的字符串将指向静态存储;调用方不应修改其值。 该值将以
sys.version
的名称供 Python 代码使用。另请参阅
Py_Version
常量。
-
const char *Py_GetPlatform()¶
- 属于 稳定 ABI.
返回当前平台的平台标识符。 在 Unix 上,这将以操作系统的“官方”名称为基础,转换为小写形式,再加上主版本号;例如,对于 Solaris 2.x,或称 SunOS 5.x,该值将为
'sunos5'
。 在 macOS 上,它将为'darwin'
。 在 Windows 上它将为'win'
。 返回的字符串指向静态存储;调用方不应修改其值。 Python 代码可通过sys.platform
获取该值。
-
const char *Py_GetCopyright()¶
- 属于 稳定 ABI.
返回当前 Python 版本的官方版权字符串,例如
'Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam'
返回的字符串指向静态存储;调用者不应修改其值。 Python 代码可通过
sys.copyright
获取该值。
-
const char *Py_GetCompiler()¶
- 属于 稳定 ABI.
返回用于编译当前 Python 版本的编译器指令,为带方括号的形式,例如:
"[GCC 2.7.2.2]"
返回的字符串指向静态存储;调用者不应修改其值。 Python 代码可以从变量
sys.version
中获取该值。
-
const char *Py_GetBuildInfo()¶
- 属于 稳定 ABI.
返回有关当前Python解释器实例的序列号和构建日期和时间的信息,例如:
"#67, Aug 1 1997, 22:34:28"
返回的字符串指向静态存储;调用者不应修改其值。 Python 代码可以从变量
sys.version
中获取该值。
-
void PySys_SetArgvEx(int argc, wchar_t **argv, int updatepath)¶
- 属于 稳定 ABI.
此 API 被保留用于向下兼容:应当改为设置
PyConfig.argv
,PyConfig.parse_argv
和PyConfig.safe_path
,参见 Python 初始化配置。根据 argc 和 argv 设置
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 初始化配置 的
PyConfig.orig_argv
和PyConfig.argv
成员。备注
建议在出于执行单个脚本以外的目的嵌入 Python 解释器的应用传入
0
作为 updatepath,并在需要时更新sys.path
本身。 参见 CVE 2008-5983。在 3.1.3 之前的版本中,你可以通过在调用
PySys_SetArgv()
之后手动弹出第一个sys.path
元素,例如使用:PyRun_SimpleString("import sys; sys.path.pop(0)\n");
Added in version 3.1.3.
自 3.11 版本弃用.
-
void PySys_SetArgv(int argc, wchar_t **argv)¶
- 属于 稳定 ABI.
此 API 仅为向下兼容而保留:应当改为设置
PyConfig.argv
并改用PyConfig.parse_argv
,参见 Python 初始化配置。此函数相当于
PySys_SetArgvEx()
设置了 updatepath 为1
除非 python 解释器启动时附带了-I
。使用
Py_DecodeLocale()
来解码字节串以得到一个 wchar_* 字符串。另请参阅 Python 初始化配置 的
PyConfig.orig_argv
和PyConfig.argv
成员。在 3.4 版本发生变更: updatepath 值依赖于
-I
。自 3.11 版本弃用.
-
void Py_SetPythonHome(const wchar_t *home)¶
- 属于 稳定 ABI.
此 API 被保留用于向下兼容:应当改为设置
PyConfig.home
,参见 Python 初始化配置。设置默认的 "home" 目录,也就是标准 Python 库所在的位置。 请参阅
PYTHONHOME
了解该参数字符串的含义。此参数应当指向静态存储中一个以零值结束的字符串,其内容在程序执行期间将保持不变。 Python 解释器中的代码绝不会修改此存储中的内容。
使用
Py_DecodeLocale()
来解码字节串以得到一个 wchar_* 字符串。自 3.11 版本弃用.
-
wchar_t *Py_GetPythonHome()¶
- 属于 稳定 ABI.
返回默认的 "home",就是由之前对
Py_SetPythonHome()
的调用所设置的值,或者在设置了PYTHONHOME
环境变量的情况下该环境变量的值。此函数不应在
Py_Initialize()
之前被调用,否则将返回NULL
。在 3.10 版本发生变更: 现在如果它在
Py_Initialize()
之前被调用将返回NULL
。
线程状态和全局解释器锁¶
Python 解释器不是完全线程安全的。 为了支持多线程的 Python 程序,设置了一个全局锁,称为 global interpreter lock 或 GIL,当前线程必须在持有它之后才能安全地访问 Python 对象。 如果没有这个锁,即使最简单的操作也可能在多线程的程序中导致问题:例如,当两个线程同时增加相同对象的引用计数时,引用计数可能最终只增加了一次而不是两次。
因此,规则要求只有获得 GIL 的线程才能在 Python对象上执行操作或调用 Python/C API 函数。 为了模拟并发执行,解释器会定期尝试切换线程 (参见 sys.setswitchinterval()
)。 锁也会在读写文件等可能造成阻塞的 I/O 操作时释放,以便其他 Python 线程可以同时运行。
Python 解释器会在一个名为 PyThreadState
的数据结构体中保存一些线程专属的记录信息。 还有一个全局变量指向当前的 PyThreadState
: 它可以使用 PyThreadState_Get()
来获取。
从扩展扩展代码中释放 GIL¶
大多数操作 GIL 的扩展代码具有以下简单结构:
将线程状态保存到一个局部变量中。
释放全局解释器锁。
... 执行某些阻塞式的 I/O 操作 ...
重新获取全局解释器锁。
从局部变量中恢复线程状态。
这是如此常用因此增加了一对宏来简化它:
Py_BEGIN_ALLOW_THREADS
... 执行某些阻塞式的 I/O 操作 ...
Py_END_ALLOW_THREADS
Py_BEGIN_ALLOW_THREADS
宏将打开一个新块并声明一个隐藏的局部变量;Py_END_ALLOW_THREADS
宏将关闭这个块。
上面的代码块可扩展为下面的代码:
PyThreadState *_save;
_save = PyEval_SaveThread();
... 执行某些阻塞式的 I/O 操作 ...
PyEval_RestoreThread(_save);
这些函数的工作原理如下:全局解释器锁被用来保护指向当前线程状态的指针。 当释放锁并保存线程状态时,必须在锁被释放之前获取当前线程状态指针 (因为另一个线程可以立即获取锁并将自己的线程状态存储到全局变量中)。 相应地,当获取锁并恢复线程状态时,必须在存储线程状态指针之前先获取锁。
非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();
/* 在此执行 Python 动作。 */
result = CallSomeFunction();
/* 评估结果或处理异常 */
/* 释放线程。 在此之后不再允许 Python API。 */
PyGILState_Release(gstate);
请注意 PyGILState_*
函数会假定只有一个全局解释器(由 Py_Initialize()
自动创建)。 Python 支持创建额外的解释器(使用 Py_NewInterpreter()
创建),但不支持混合使用多个解释器和 PyGILState_*
API。
有关 fork() 的注意事项¶
有关线程的另一个需要注意的重要问题是它们在面对 C fork()
调用时的行为。 在大多数支持 fork()
的系统中,当一个进程执行 fork 之后将只有发出 fork 的线程存在。 这对需要如何处理锁以及CPython 的运行时内所有的存储状态都会有实质性的影响。
只保留“当前”线程这一事实意味着任何由其他线程所持有的锁永远不会被释放。 Python 通过在 fork 之前获取内部使用的锁,并随后释放它们的方式为 os.fork()
解决了这个问题。 此外,它还会重置子进程中的任何 锁对象。 在扩展或嵌入 Python 时,没有办法通知 Python 在 fork 之前或之后需要获取或重置的附加(非 Python)锁。 需要使用 OS 工具例如 pthread_atfork()
来完成同样的事情。 此外,在扩展或嵌入 Python 时,直接调用 fork()
而不是通过 os.fork()
(并返回到或调用至 Python 中) 调用可能会导致某个被 fork 之后失效的线程所持有的 Python 内部锁发生死锁。 PyOS_AfterFork_Child()
会尝试重置必要的锁,但并不总是能够做到。
所有其他线程都将结束这一事实也意味着 CPython 的运行时状态必须妥善清理,os.fork()
就是这样做的。 这意味着最终化归属于当前解释器的所有其他 PyThreadState
对象以及所有其他 PyInterpreterState
对象。 由于这一点以及 "main" 解释器 的特殊性质,fork()
应当只在该解释器 的 "main" 线程中被调用,而 CPython 全局运行时最初就是在该线程中初始化的。 只有当 exec()
将随后立即被调用的情况是唯一的例外。
高阶 API¶
这些是在编写 C 扩展代码或在嵌入 Python 解释器时最常用的类型和函数:
-
type PyInterpreterState¶
- 属于 受限 API (作为不透明的结构体).
该数据结构代表多个合作线程所共享的状态。 属于同一解释器的线程将共享其模块管理以及其他一些内部条目。 该结构体中不包含公有成员。
最初归属于不同解释器的线程不会共享任何东西,但进程状态如可用内存、打开的文件描述符等等除外。 全局解释器锁也会被所有线程共享,无论它们归属于哪个解释器。
-
type PyThreadState¶
- 属于 受限 API (作为不透明的结构体).
该数据结构代表单个线程的状态。 唯一的公有数据成员为:
-
PyInterpreterState *interp¶
该线程的解释器状态。
-
PyInterpreterState *interp¶
-
void PyEval_InitThreads()¶
- 属于 稳定 ABI.
不执行任何操作的已弃用函数。
在 Python 3.6 及更老的版本中,此函数会在 GIL 不存在时创建它。
在 3.9 版本发生变更: 此函数现在不执行任何操作。
在 3.7 版本发生变更: 该函数现在由
Py_Initialize()
调用,因此你无需再自行调用它。在 3.2 版本发生变更: 此函数已不再被允许在
Py_Initialize()
之前调用。自 3.9 版本弃用.
-
int PyEval_ThreadsInitialized()¶
- 属于 稳定 ABI.
如果
PyEval_InitThreads()
已经被调用则返回非零值。 此函数可在不持有 GIL 的情况下被调用,因而可被用来避免在单线程运行时对加锁 API 的调用。在 3.7 版本发生变更: 现在 GIL 将由
Py_Initialize()
来初始化。自 3.9 版本弃用.
-
PyThreadState *PyEval_SaveThread()¶
- 属于 稳定 ABI.
释放全局解释器锁 (如果已创建) 并将线程状态重置为
NULL
,返回之前的线程状态 (不为NULL
)。 如果锁已被创建,则当前线程必须已获取到它。
-
void PyEval_RestoreThread(PyThreadState *tstate)¶
- 属于 稳定 ABI.
获取全局解释器锁 (如果已创建) 并将线程状态设为 tstate,它必须不为
NULL
。 如果锁已被创建,则当前线程必须尚未获取它,否则将发生死锁。备注
当运行时正在最终化时从某个线程调用此函数将终结该线程,即使线程不是由 Python 创建的。 你可以在调用此函数之前使用
_Py_IsFinalizing()
或sys.is_finalizing()
来检查解释器是否还处于最终化过程中以避免不必要的终结。
-
PyThreadState *PyThreadState_Get()¶
- 属于 稳定 ABI.
返回当前线程状态。 全局解释器锁必须被持有。 在当前状态为
NULL
时,这将发出一个致命错误 (这样调用方将无须检查是否为NULL
)。
-
PyThreadState *PyThreadState_Swap(PyThreadState *tstate)¶
- 属于 稳定 ABI.
交换当前线程状态与由参数 tstate (可能为
NULL
) 给出的线程状态。 全局解释器锁必须被持有且未被释放。
下列函数使用线程级本地存储,并且不能兼容子解释器:
-
PyGILState_STATE PyGILState_Ensure()¶
- 属于 稳定 ABI.
确保当前线程已准备好调用 Python C API 而不管 Python 或全局解释器锁的当前状态如何。 只要每次调用都与
PyGILState_Release()
的调用相匹配就可以通过线程调用此函数任意多次。 一般来说,只要线程状态恢复到 Release() 之前的状态就可以在PyGILState_Ensure()
和PyGILState_Release()
调用之间使用其他与线程相关的 API。 例如,可以正常使用Py_BEGIN_ALLOW_THREADS
和Py_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)¶
- 属于 稳定 ABI.
释放之前获取的任何资源。 在此调用之后,Python 的状态将与其在对相应
PyGILState_Ensure()
调用之前的一样(但是通常此状态对调用方来说将是未知的,对 GILState API 的使用也是如此)。对
PyGILState_Ensure()
的每次调用都必须与在同一线程上对PyGILState_Release()
的调用相匹配。
-
PyThreadState *PyGILState_GetThisThreadState()¶
- 属于 稳定 ABI.
获取此线程的当前线程状态。 如果当前线程上没有使用过 GILState API 则可以返回
NULL
。 请注意主线程总是会有这样一个线程状态,即使没有在主线程上执行过自动线程状态调用。 这主要是一个辅助/诊断函数。
-
int PyGILState_Check()¶
如果当前线程持有 GIL 则返回
1
否则返回0
。 此函数可以随时从任何线程调用。 只有当它的 Python 线程状态已经初始化并且当前持有 GIL 时它才会返回1
。 这主要是一个辅助/诊断函数。 例如在回调上下文或内存分配函数中会很有用处,当知道 GIL 被锁定时可以允许调用方执行敏感的操作或是在其他情况下做出不同的行为。Added in version 3.4.
以下的宏被使用时通常不带末尾分号;请在 Python 源代码发布包中查看示例用法。
-
Py_BEGIN_ALLOW_THREADS¶
- 属于 稳定 ABI.
此宏会扩展为
{ PyThreadState *_save; _save = PyEval_SaveThread();
。 请注意它包含一个开头花括号;它必须与后面的Py_END_ALLOW_THREADS
宏匹配。 有关此宏的进一步讨论请参阅上文。
-
Py_END_ALLOW_THREADS¶
- 属于 稳定 ABI.
此宏扩展为
PyEval_RestoreThread(_save); }
。 注意它包含一个右花括号;它必须与之前的Py_BEGIN_ALLOW_THREADS
宏匹配。 请参阅上文以进一步讨论此宏。
-
Py_BLOCK_THREADS¶
- 属于 稳定 ABI.
这个宏扩展为
PyEval_RestoreThread(_save);
: 它等价于没有关闭花括号的Py_END_ALLOW_THREADS
。
-
Py_UNBLOCK_THREADS¶
- 属于 稳定 ABI.
这个宏扩展为
_save = PyEval_SaveThread();
: 它等价于没有开始花括号和变量声明的Py_BEGIN_ALLOW_THREADS
。
底层级 API¶
下列所有函数都必须在 Py_Initialize()
之后被调用。
在 3.7 版本发生变更: Py_Initialize()
现在会初始化 GIL。
-
PyInterpreterState *PyInterpreterState_New()¶
- 属于 稳定 ABI.
创建一个新的解释器状态对象。 不需要持有全局解释器锁,但如果有必要序列化对此函数的调用则可能会持有。
引发一个不带参数的 审计事件
cpython.PyInterpreterState_New
。
-
void PyInterpreterState_Clear(PyInterpreterState *interp)¶
- 属于 稳定 ABI.
重置解释器状态对象中的所有信息。 必须持有全局解释器锁。
引发一个不带参数的 审计事件
cpython.PyInterpreterState_Clear
。
-
void PyInterpreterState_Delete(PyInterpreterState *interp)¶
- 属于 稳定 ABI.
销毁解释器状态对象。 不需要持有全局解释器锁。 解释器状态必须使用之前对
PyInterpreterState_Clear()
的调用来重置。
-
PyThreadState *PyThreadState_New(PyInterpreterState *interp)¶
- 属于 稳定 ABI.
创建属于给定解释器对象的新线程状态对象。全局解释器锁不需要保持,但如果需要序列化对此函数的调用,则可以保持。
-
void PyThreadState_Clear(PyThreadState *tstate)¶
- 属于 稳定 ABI.
重置线程状态对象中的所有信息。 必须持有全局解释器锁。
在 3.9 版本发生变更: 此函数现在会调用
PyThreadState.on_delete
回调。 在之前版本中,此操作是发生在PyThreadState_Delete()
中的。
-
void PyThreadState_Delete(PyThreadState *tstate)¶
- 属于 稳定 ABI.
销毁线程状态对象。 不需要持有全局解释器锁。 线程状态必须使用之前对
PyThreadState_Clear()
的调用来重置。
-
void PyThreadState_DeleteCurrent(void)¶
销毁当前线程状态并释放全局解释器锁。 与
PyThreadState_Delete()
类似,必须持有全局解释器锁。 线程状态必须已通过之前对PyThreadState_Clear()
的调用来重置。
-
PyFrameObject *PyThreadState_GetFrame(PyThreadState *tstate)¶
- 属于 稳定 ABI 自 3.10 版起.
获取 Python 线程状态 tstate 的当前帧。
返回一个 strong reference。 如果没有当前执行的帧则返回
NULL
。另请参阅
PyEval_GetFrame()
。tstate 必须不为
NULL
。Added in version 3.9.
-
uint64_t PyThreadState_GetID(PyThreadState *tstate)¶
- 属于 稳定 ABI 自 3.10 版起.
获取 Python 线程状态 tstate 的唯一线程状态标识符。
tstate 必须不为
NULL
。Added in version 3.9.
-
PyInterpreterState *PyThreadState_GetInterpreter(PyThreadState *tstate)¶
- 属于 稳定 ABI 自 3.10 版起.
获取 Python 线程状态 tstate 对应的解释器。
tstate 必须不为
NULL
。Added in version 3.9.
-
void PyThreadState_EnterTracing(PyThreadState *tstate)¶
暂停 Python 线程状态 tstate 中的追踪和性能分析。
使用
PyThreadState_LeaveTracing()
函数来恢复它们。Added in version 3.11.
-
void PyThreadState_LeaveTracing(PyThreadState *tstate)¶
恢复 Python 线程状态 tstate 中被
PyThreadState_EnterTracing()
函数暂停的追踪和性能分析。另请参阅
PyEval_SetTrace()
和PyEval_SetProfile()
函数。Added in version 3.11.
-
PyInterpreterState *PyInterpreterState_Get(void)¶
- 属于 稳定 ABI 自 3.9 版起.
获取当前解释器。
如果不存在当前 Python 线程状态或不存在当前解释器则将发出致命级错误信号。 它无法返回 NULL。
调用时必须携带GIL。
Added in version 3.9.
-
int64_t PyInterpreterState_GetID(PyInterpreterState *interp)¶
- 属于 稳定 ABI 自 3.7 版起.
返回解释器的唯一 ID。 如果执行过程中发生任何错误则将返回
-1
并设置错误。调用时必须携带GIL。
Added in version 3.7.
-
PyObject *PyInterpreterState_GetDict(PyInterpreterState *interp)¶
- 属于 稳定 ABI 自 3.8 版起.
返回一个存储解释器专属数据的字典。 如果此函数返回
NULL
则没有任何异常被引发并且调用方应当将解释器专属字典视为不可用。这不是
PyModule_GetState()
的替代,扩展仍应使用它来存储解释器专属的状态信息。Added in version 3.8.
-
typedef PyObject *(*_PyFrameEvalFunction)(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag)¶
帧评估函数的类型
throwflag 形参将由生成器的
throw()
方法来使用:如为非零值,则处理当前异常。在 3.9 版本发生变更: 此函数现在可接受一个 tstate 形参。
在 3.11 版本发生变更: frame 形参由
PyFrameObject*
改为_PyInterpreterFrame*
。
-
_PyFrameEvalFunction _PyInterpreterState_GetEvalFrameFunc(PyInterpreterState *interp)¶
获取帧评估函数。
请参阅 PEP 523 "Adding a frame evaluation API to CPython"。
Added in version 3.9.
-
void _PyInterpreterState_SetEvalFrameFunc(PyInterpreterState *interp, _PyFrameEvalFunction eval_frame)¶
设置帧评估函数。
请参阅 PEP 523 "Adding a frame evaluation API to CPython"。
Added in version 3.9.
-
PyObject *PyThreadState_GetDict()¶
- 返回值:借入的引用。 属于 稳定 ABI.
返回一个扩展可以在其中存储线程专属状态信息的字典。 每个扩展都应当使用一个独有的键用来在该字典中存储状态。 在没有可用的当前线程状态时也可以调用此函数。 如果此函数返回
NULL
,则还没有任何异常被引发并且调用方应当假定没有可用的当前线程状态。
-
int PyThreadState_SetAsyncExc(unsigned long id, PyObject *exc)¶
- 属于 稳定 ABI.
在一个线程中异步地引发异常。 id 参数是目标线程的线程 id;exc 是要引发的异常对象。 该函数不会窃取任何对 exc 的引用。 为防止随意滥用,你必须编写你自己的 C 扩展来调用它。 调用时必须持有 GIL。 返回已修改的线程状态数量;该值通常为一,但如果未找到线程 id 则会返回 0。 如果 exc 为``NULL``,则会清除线程的待处理异常(如果存在)。 这将不会引发异常。
在 3.7 版本发生变更: id 形参的类型已从 long 变为 unsigned long。
-
void PyEval_AcquireThread(PyThreadState *tstate)¶
- 属于 稳定 ABI.
获取全局解释器锁并将当前线程状态设为 tstate,它必须不为
NULL
。 锁必须在此之前已被创建。 如果该线程已获取锁,则会发生死锁。备注
当运行时正在最终化时从某个线程调用此函数将终结该线程,即使线程不是由 Python 创建的。 你可以在调用此函数之前使用
_Py_IsFinalizing()
或sys.is_finalizing()
来检查解释器是否还处于最终化过程中以避免不必要的终结。在 3.8 版本发生变更: 已被更新为与
PyEval_RestoreThread()
,Py_END_ALLOW_THREADS()
和PyGILState_Ensure()
保持一致,如果在解释器正在最终化时被调用则会终结当前线程。PyEval_RestoreThread()
是一个始终可用的(即使线程尚未初始化)更高层级函数。
-
void PyEval_ReleaseThread(PyThreadState *tstate)¶
- 属于 稳定 ABI.
将当前线程状态重置为
NULL
并释放全局解释器锁。 在此之前锁必须已被创建并且必须由当前的线程所持有。 tstate 参数必须不为NULL
,该参数仅被用于检查它是否代表当前线程状态 --- 如果不是,则会报告一个致命级错误。PyEval_SaveThread()
是一个始终可用的(即使线程尚未初始化)更高层级函数。
-
void PyEval_AcquireLock()¶
- 属于 稳定 ABI.
获取全局解释器锁。锁必须在此之前已被创建。 如果该线程已经拥有锁,则会出现死锁。
自 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()¶
- 属于 稳定 ABI.
释放全局解释器锁。 锁必须在此之前已被创建。
自 3.2 版本弃用: 此函数不会更新当前线程状态。 请改用
PyEval_SaveThread()
或PyEval_ReleaseThread()
。
子解释器支持¶
虽然在大多数用例中,你都只会嵌入一个单独的 Python 解释器,但某些场景需要你在同一个进程甚至同一个线程中创建多个独立的解释器。 子解释器让你能够做到这一点。
“主”解释器是在运行时初始化时创建的第一个解释器。 它通常是一个进程中唯一的 Python 解释器。 与子解释器不同,主解释器具有唯一的进程全局责任比如信号处理等。 它还负责在运行时初始化期间的执行并且通常还是运行时最终化期间的活动解释器。 PyInterpreterState_Main()
函数将返回一个指向其状态的指针。
你可以使用 PyThreadState_Swap()
函数在子解释器之间进行切换。 你可以使用下列函数来创建和销毁它们:
-
type PyInterpreterConfig¶
包含用于配置子解释器的大部分形参的结构体。 其值仅在
Py_NewInterpreterFromConfig()
中被使用而绝不会被运行时所修改。Added in version 3.12.
结构体字段:
-
int use_main_obmalloc¶
如果该值为
0
则子解释器将使用自己的“对象”分配器状态。 否则它将使用(共享)主解释器的状态。如果该值为
0
则check_multi_interp_extensions
必须为1
(非零值)。 如果该值为1
则gil
不可为PyInterpreterConfig_OWN_GIL
。
-
int allow_fork¶
如果该值为
0
则运行时将不支持在当前激活了子解释器的任何线程中 fork 进程。 否则 fork 将不受限制。请注意当 fork 被禁止时
subprocess
模块将仍然可用。
-
int allow_exec¶
如果该值为
0
则运行时将不支持在当前激活了子解释器的任何线程中通过 exec (例如os.execv()
) 替换当前进程。 否则 exec 将不受限制。请注意当 exec 被禁止时
subprocess
模块将仍然可用。
-
int allow_daemon_threads¶
如果该值为
0
则子解释器的threading
模块将不会创建守护线程。 否则将允许守护线程(只要allow_threads
是非零值)。
-
int check_multi_interp_extensions¶
如果该值为
0
则所有扩展模块均可在当前子解释器被激活的任何线程中被导入,包括旧式的 (单阶段初始化) 模块。 否则将只有多阶段初始化扩展模块 (参见 PEP 489) 可以被导入。 (另请参阅Py_mod_multiple_interpreters
。)如果
use_main_obmalloc
为0
则该值必须为1
(非零值)。
-
int gil¶
这将确定针对子解释器的 GIL 操作方式。 它可以是以下的几种之一:
-
PyInterpreterConfig_DEFAULT_GIL¶
使用默认选择 (
PyInterpreterConfig_SHARED_GIL
)。
-
PyInterpreterConfig_SHARED_GIL¶
使用(共享)主解释器的 GIL。
-
PyInterpreterConfig_OWN_GIL¶
使用子解释器自己的 GIL。
如果该值为
PyInterpreterConfig_OWN_GIL
则PyInterpreterConfig.use_main_obmalloc
必须为0
。-
PyInterpreterConfig_DEFAULT_GIL¶
-
int use_main_obmalloc¶
-
PyStatus Py_NewInterpreterFromConfig(PyThreadState **tstate_p, const PyInterpreterConfig *config)¶
新建一个子解释器。 这是一个 (几乎) 完全隔离的 Python 代码执行环境。 特别需要注意,新的子解释器具有全部已导入模块的隔离的、独立的版本,包括基本模块
builtins
,__main__
和sys
等。 已加载模块表 (sys.modules
) 和模块搜索路径 (sys.path
) 也是隔离的。 新环境没有sys.argv
变量。 它具有新的标准 I/O 流文件对象sys.stdin
,sys.stdout
和sys.stderr
(不过这些对象都指向相同的底层文件描述符)。给定的 config 控制着初始化解释器所使用的选项。
成功后,tstate_p 将被设为新的子解释器中创建的第一个线程状态。该线程状态是在当前线程状态中创建的。 请注意并没有真实的线程被创建;请参阅下文有关线程状态的讨论。 如果创建新的解释器没有成功,则 tstate_p 将被设为
NULL
;不会设置任何异常因为异常状态是存储在当前的线程状态中而当前线程状态并不一定存在。与所有其他 Python/C API 函数一样,在调用此函数之前必须先持有全局解释器锁并且在其返回时仍继续持有。 同样地在进入函数时也必须设置当前线程状态。 执行成功后,返回的线程状态将被设为当前线程状态。 如果创建的子解释器具有自己的 GIL 那么调用方解释器的 GIL 将被释放。 当此函数返回时,新的解释器的 GIL 将由当前线程持有而之前的解释器的 GIL 在此将保持释放状态。
Added in version 3.12.
子解释器在彼此相互隔离,并让特定功能受限的情况下是最有效率的:
PyInterpreterConfig config = { .use_main_obmalloc = 0, .allow_fork = 0, .allow_exec = 0, .allow_threads = 1, .allow_daemon_threads = 0, .check_multi_interp_extensions = 1, .gil = PyInterpreterConfig_OWN_GIL, }; PyThreadState *tstate = Py_NewInterpreterFromConfig(&config);
请注意该配置只会被短暂使用而不会被修改。 在初始化期间配置的值会被转换成各种
PyInterpreterState
值。 配置的只读副本可以被内部存储于PyInterpreterState
中。扩展模块将以如下方式在(子)解释器之间共享:
对于使用多阶段初始化的模块 ,例如
PyModule_FromDefAndSpec()
,将为每个解释器创建并初始化一个单独的模块对象。 只有 C 层级的静态和全局变量能在这些模块 对象之间共享。对于使用单阶段初始化的模块,例如
PyModule_Create()
,当特定扩展被首次导入时,它将被正常初始化,并会保存其模块字典的一个 (浅) 拷贝。 当同一扩展被另一个 (子) 解释器导入时,将初始化一个新模块并填充该拷贝的内容;扩展的init
函数不会被调用。 因此模块字典中的对象最终会被 (子) 解释器所共享,这可能会导致预期之外的行为 (参见下文的 Bugs and caveats)。请注意这不同于在调用
Py_FinalizeEx()
和Py_Initialize()
完全重新初始化解释器之后导入扩展时所发生的情况;对于那种情况,扩展的initmodule
函数 会被 再次调用。 与多阶段初始化一样,这意味着只有 C 层级的静态和全局变量能在这些模块之间共享。
-
PyThreadState *Py_NewInterpreter(void)¶
- 属于 稳定 ABI.
新建一个子解释器。 这在本质上只是针对
Py_NewInterpreterFromConfig()
的包装器,其配置保留了现有的行为。 结果是一个未隔离的子解释器,它会共享主解释器的 GIL,允许 fork/exec,允许守护线程,也允许单阶段初始化模块。
-
void Py_EndInterpreter(PyThreadState *tstate)¶
- 属于 稳定 ABI.
销毁由给定的线程状态所代表的(子)解释器。 给定的线程状态必须为当前的线程状态。 请参阅下文中关于线程状态的讨论。 当调用返回时,当前的线程状态将为
NULL
。 与此解释器相关联的所有线程状态都会被销毁。 在调用此函数之前必须持有目标解释器所使用的全局解释器锁。 当其返回时将不再持有 GIL。Py_FinalizeEx()
将销毁所有在当前时间点上尚未被明确销毁的子解释器。
解释器级 GIL¶
使用 Py_NewInterpreterFromConfig()
你将可以创建一个与其他解释器完全隔离的子解释器,包括具有自己的 GIL。 这种隔离带来的最大好处在于这样的解释器执行 Python 代码时不会被其他解释器所阻塞或者阻塞任何其他解释器。 因此在运行 Python 代码时单个 Python 进程可以真正地利用多个 CPU 核心。 这种隔离还能鼓励开发者采取不同于仅使用线程的并发方式。 (参见 PEP 554)。
使用隔离的解释器要求谨慎地保持隔离状态。 尤其是意味着不要在未确保线程安全的情况下共享任何对象 或可变的状态。 由于引用计数的存在即使是在其他情况下不可变的对象 (例如 None
, (1, 5)
) 通常也不可被共享。 针对此问题的一种简单但效率较低的解决方式是在使用某些状态 (或对象) 时总是使用一个全局锁。 或者,对于实际上不可变的对象 (如整数或字符串) 可以通过将其设为 "永久" 对象而无视其引用计数来确保其安全。 事实上,对于内置单例、小整数和其他一些内置对象都是这样做的。
如果你能保持隔离状态那么你将能获得真正的多核计算能力而不会遇到自由线程所带来的复杂性。 如果未能保持隔离状态那么你将面对自由线程所带来的全部后果,包括线程竞争和难以调试的崩溃。
除此之外,使用多个相互隔离的解释器的一个主要挑战是如何在它们之间安全 (不破坏隔离状态)、高效地进行通信。 运行时和标准库还没有为此提供任何标准方式。 未来的标准库模块将会帮助减少保持隔离状态所需的工作量并为解释器之间的数据通信(和共享)公开有效的工具。
Added in version 3.12.
错误和警告¶
由于子解释器 (以及主解释器) 都是同一个进程的组成部分,它们之间的隔离状态并非完美 --- 举例来说,使用低层级的文件操作如 os.close()
时它们可能 (无意或恶意地) 影响它们各自打开的文件。 由于 (子) 解释器之间共享扩展的方式,某些扩展可能无法正常工作;在使用单阶段初始化或者 (静态) 全局变量时尤其如此。 在一个子解释器中创建的对象有可能被插入到另一个 (子) 解释器的命名空间中;这种情况应当尽可能地避免。
应当特别注意避免在子解释器之间共享用户自定义的函数、方法、实例或类,因为由这些对象执行的导入 操作可能会影响错误的已加载模块的 (子) 解释器的字典。 同样重要的一点是应当避免共享可被上述对象访问的对象 。
还要注意的一点是将此功能与 PyGILState_*
API 结合使用是很微妙的,因为这些 API 会假定 Python线程状态与操作系统级线程之间存在双向投影关系,而子解释器的存在打破了这一假定。 强烈建议你不要在一对互相匹配的 PyGILState_Ensure()
和 PyGILState_Release()
调用之间切换子解释器。 此外,使用这些 API 以允许从非 Python 创建的线程调用 Python 代码的扩展 (如 ctypes
) 在使用子解释器时很可能会出现问题。
异步通知¶
提供了一种向主解释器线程发送异步通知的机制。 这些通知将采用函数指针和空指针参数的形式。
-
int Py_AddPendingCall(int (*func)(void*), void *arg)¶
- 属于 稳定 ABI.
将一个函数加入从主解释器线程调用的计划任务。 成功时,将返回
0
并将 func 加入要被主线程调用的等待队列。 失败时,将返回-1
但不会设置任何异常。当成功加入队列后,func 将 最终 附带参数 arg 被主解释器线程调用。 对于正常运行的 Python 代码来说它将被异步地调用,但要同时满足以下两个条件:
位于 bytecode 的边界上;
主线程持有 global interpreter lock (因此 func 可以使用完整的 C API)。
func 必须在成功时返回
0
,或在失败时返回-1
并设置一个异常集合。 func 不会被中断来递归地执行另一个异步通知,但如果全局解释器锁被释放则它仍可被中断以切换线程。此函数的运行不需要当前线程状态,也不需要全局解释器锁。
要在子解释器中调用函数,调用方必须持有 GIL。 否则,函数 func 可能会被安排给错误的解释器来调用。
警告
这是一个低层级函数,只在非常特殊的情况下有用。 不能保证 func 会尽快被调用。 如果主线程忙于执行某个系统调用,func 将不会在系统调用返回之前被调用。 此函数 通常 不适合 从任意 C 线程调用 Python 代码。 作为替代,请使用 PyGILStateAPI。
Added in version 3.1.
在 3.9 版本发生变更: 如果此函数在子解释器中被调用,则函数 func 将被安排在子解释器中调用,而不是在主解释器中调用。现在每个子解释器都有自己的计划调用列表。
分析和跟踪¶
Python 解释器为附加的性能分析和执行跟踪工具提供了一些低层级的支持。 它们可被用于性能分析、调试和覆盖分析工具。
这个 C 接口允许性能分析或跟踪代码避免调用 Python 层级的可调用对象带来的开销,它能直接执行 C 函数调用。 此工具的基本属性没有变化;这个接口允许针对每个线程安装跟踪函数,并且向跟踪函数报告的基本事件与之前版本中向 Python 层级跟踪函数报告的事件相同。
-
typedef 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_RETURN
或PyTrace_OPCODE
中的一个,而 arg 将依赖于 what 的值:what 的值
arg 的含义
总是
Py_None
.sys.exc_info()
返回的异常信息。总是
Py_None
.返回给调用方的值,或者如果是由异常导致的则返回
NULL
。正在调用函数对象。
正在调用函数对象。
正在调用函数对象。
总是
Py_None
.
-
int PyTrace_CALL¶
当对一个函数或方法的新调用被报告,或是向一个生成器增加新条目时传给
Py_tracefunc
函数的 what 形参的值。 请注意针对生成器函数的迭代器的创建情况不会被报告因为在相应的帧中没有向 Python字节码转移控制权。
-
int PyTrace_EXCEPTION¶
当一个异常被引发时传给
Py_tracefunc
函数的 what 形参的值。 在处理完任何字节码之后将附带 what 的值调用回调函数,在此之后该异常将会被设置在正在执行的帧中。 这样做的效果是当异常传播导致 Python 栈展开时,被调用的回调函数将随异常传播返回到每个帧。 只有跟踪函数才会接收到这些事件;性能分析器并不需要它们。
-
int PyTrace_LINE¶
当一个行编号事件被报告时传给
Py_tracefunc
函数 (但不会传给性能分析函数) 的 what 形参的值。 它可以通过将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 形参的值。 在默认情况下此事件不会被发送:它必须通过在某个帧上将f_trace_opcodes
设为 1 来显式地请求。
-
void PyEval_SetProfile(Py_tracefunc func, PyObject *obj)¶
将性能分析器函数设为 func。 obj 形参将作为第一个形参传给该函数,它可以是任意 Python 对象或为
NULL
。 如果性能分析函数需要维护状态,则为每个线程的 obj 使用不同的值将提供一个方便而线程安全的存储位置。 这个性能分析函数将针对除PyTrace_LINE
PyTrace_OPCODE
和PyTrace_EXCEPTION
以外的所有被监控事件进行调用。另请参阅
sys.setprofile()
函数。调用方必须持有 GIL。
-
void PyEval_SetProfileAllThreads(Py_tracefunc func, PyObject *obj)¶
类似于
PyEval_SetProfile()
但会在属于当前解释器的所有在运行线程中设置性能分析函数而不是仅在当前线程上设置。调用方必须持有 GIL。
与
PyEval_SetProfile()
一样,该函数会忽略任何被引发的异常同时在所有线程中设置性能分析函数。
Added in version 3.12.
-
void PyEval_SetTrace(Py_tracefunc func, PyObject *obj)¶
将跟踪函数设为 func。 这类似于
PyEval_SetProfile()
,区别在于跟踪函数会接收行编号事件和操作码级事件,但不会接收与被调用的 C 函数对象相关的任何事件。 使用PyEval_SetTrace()
注册的任何跟踪函数将不会接收PyTrace_C_CALL
、PyTrace_C_EXCEPTION
或PyTrace_C_RETURN
作为 what 形参的值。另请参阅
sys.settrace()
函数。调用方必须持有 GIL。
-
void PyEval_SetTraceAllThreads(Py_tracefunc func, PyObject *obj)¶
类似于
PyEval_SetTrace()
但会在属于当前解释器的所有在运行线程中设置跟踪函数而不是仅在当前线程上设置。调用方必须持有 GIL。
与
PyEval_SetTrace()
一样,该函数会忽略任何被引发的异常同时在所有线程中设置跟踪函数。
Added in version 3.12.
高级调试器支持¶
这些函数仅供高级调试工具使用。
-
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 之后的下一项。
线程本地存储支持¶
Python 解释器提供也对线程本地存储 (TLS) 的低层级支持,它对下层的原生 TLS 实现进行了包装以支持 Python 层级的线程本地存储 API (threading.local
)。 CPython 的 C 层级 API 与 pthreads 和 Windows 所提供的类似:使用一个线程键和函数来为每个线程关联一个 void* 值。
当调用这些函数时 无须 持有 GIL;它们会提供自己的锁机制。
请注意 Python.h
并不包括 TLS API 的声明,你需要包括 pythread.h
来使用线程本地存储。
备注
这些 API 函数都不会为 void* 的值处理内存管理问题。 你需要自己分配和释放它们。 如果 void* 值碰巧为 PyObject*,这些函数也不会对它们执行引用计数操作。
线程专属存储 (TSS) API¶
引入 TSSAPI 是为了取代 CPython 解释器中现有 TLS API 的使用。 该 API 使用一个新类型 Py_tss_t
而不是 int 来表示线程键。
Added in version 3.7.
参见
"A New C-API for Thread-Local Storage in CPython" (PEP 539)
-
type Py_tss_t¶
该数据结构表示线程键的状态,其定义可能依赖于下层的 TLS 实现,并且它有一个表示键初始化状态的内部字段。 该结构体中不存在公有成员。
当未定义 Py_LIMITED_API 时,允许由
Py_tss_NEEDS_INIT
执行此类型的静态分配。
-
Py_tss_NEEDS_INIT¶
这个宏将扩展为
Py_tss_t
变量的初始化器。 请注意这个宏不会用 Py_LIMITED_API 来定义。
动态分配¶
Py_tss_t
的动态分配,在使用 Py_LIMITED_API 编译的扩展模块中是必须的,在这些模块由于此类型的实现在编译时是不透明的因此它不可能静态分配。
-
Py_tss_t *PyThread_tss_alloc()¶
- 属于 稳定 ABI 自 3.7 版起.
返回一个与使用
Py_tss_NEEDS_INIT
初始化的值的状态相同的值,或者当动态分配失败时则返回NULL
。
-
void PyThread_tss_free(Py_tss_t *key)¶
- 属于 稳定 ABI 自 3.7 版起.
在首次调用
PyThread_tss_delete()
以确保任何相关联的线程局部变量已被撤销赋值之后释放由PyThread_tss_alloc()
所分配的给定的 key。 如果 key 参数为NULL
则这将无任何操作。备注
被释放的 key 将变成一个悬空指针。 你应当将 key 重置为
NULL
。
方法¶
这些函数的形参 key 不可为 NULL
。 并且,如果给定的 Py_tss_t
还未被 PyThread_tss_create()
初始化则 PyThread_tss_set()
和 PyThread_tss_get()
的行为将是未定义的。
-
int PyThread_tss_is_created(Py_tss_t *key)¶
- 属于 稳定 ABI 自 3.7 版起.
如果给定的
Py_tss_t
已通过has been initialized byPyThread_tss_create()
被初始化则返回一个非零值。
-
int PyThread_tss_create(Py_tss_t *key)¶
- 属于 稳定 ABI 自 3.7 版起.
当成功初始化一个 TSS 键时将返回零值。 如果 key 参数所指向的值未被
Py_tss_NEEDS_INIT
初始化则其行为是未定义的。 此函数可在相同的键上重复调用 -- 在已初始化的键上调用它将不执行任何操作并立即成功返回。
-
void PyThread_tss_delete(Py_tss_t *key)¶
- 属于 稳定 ABI 自 3.7 版起.
销毁一个 TSS 键以便在所有线程中遗忘与该键相关联的值,并将该键的初始化状态改为未初始化的。 已销毁的键可以通过
PyThread_tss_create()
再次被初始化。 此函数可以在同一个键上重复调用 -- 但在一个已被销毁的键上调用将是无效的。
线程本地存储 (TLS) API¶
自 3.7 版本弃用: 此 API 已被 线程专属存储 (TSS) API 所取代。
备注
这个 API 版本不支持原生 TLS 键采用无法被安全转换为 int
的的定义方式的平台。 在这样的平台上,PyThread_create_key()
将立即返回一个失败状态,并且其他 TLS 函数在这样的平台上也都无效。
由于上面提到的兼容性问题,不应在新代码中使用此版本的API。