初始化、終結和執行緒¶
有關如何在初始化前配置直譯器的詳細資訊,請參閱Python 初始化配置。
Python 初始化之前¶
在嵌入 Python 的應用程式中,必須在使用任何其他 Python/C API 函式之前呼叫 Py_Initialize()
函式;少數函式和全域性配置變數除外。
以下函式可以在 Python 初始化之前安全呼叫:
初始化直譯器的函式
配置函式
資訊函式
工具
Python 初始化配置中涵蓋的狀態報告和實用函式
記憶體分配器
同步
備註
儘管它們與上面列出的一些函式表面上相似,但直譯器初始化之前**不應呼叫**以下函式:Py_EncodeLocale()
、Py_GetPath()
、Py_GetPrefix()
、Py_GetExecPrefix()
、Py_GetProgramFullPath()
、Py_GetPythonHome()
、Py_GetProgramName()
、PyEval_InitThreads()
和 Py_RunMain()
。
全域性配置變數¶
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 版本起已棄用,將於 3.15 版本中移除。
-
int Py_DebugFlag¶
此 API 僅為向後相容而保留:應改用設定
PyConfig.parser_debug
,詳見Python 初始化配置。開啟解析器除錯輸出(僅供專家使用,取決於編譯選項)。
由
-d
選項和PYTHONDEBUG
環境變數設定。自 3.12 版本起已棄用,將於 3.15 版本中移除。
-
int Py_DontWriteBytecodeFlag¶
此 API 僅為向後相容而保留:應改用設定
PyConfig.write_bytecode
,詳見Python 初始化配置。如果設定為非零,Python 將不會在匯入源模組時嘗試寫入
.pyc
檔案。由
-B
選項和PYTHONDONTWRITEBYTECODE
環境變數設定。自 3.12 版本起已棄用,將於 3.15 版本中移除。
-
int Py_FrozenFlag¶
此 API 僅為向後相容而保留:應改用設定
PyConfig.pathconfig_warnings
,詳見Python 初始化配置。在
Py_GetPath()
中計算模組搜尋路徑時抑制錯誤訊息。由
_freeze_module
和frozenmain
程式使用的私有標誌。自 3.12 版本起已棄用,將於 3.15 版本中移除。
-
int Py_HashRandomizationFlag¶
此 API 僅為向後相容而保留:應改用設定
PyConfig.hash_seed
和PyConfig.use_hash_seed
,詳見Python 初始化配置。如果
PYTHONHASHSEED
環境變數設定為非空字串,則設定為1
。如果標誌非零,則讀取
PYTHONHASHSEED
環境變數以初始化秘密雜湊種子。自 3.12 版本起已棄用,將於 3.15 版本中移除。
-
int Py_IgnoreEnvironmentFlag¶
此 API 僅為向後相容而保留:應改用設定
PyConfig.use_environment
,詳見Python 初始化配置。忽略所有可能設定的
PYTHON*
環境變數,例如PYTHONPATH
和PYTHONHOME
。自 3.12 版本起已棄用,將於 3.15 版本中移除。
-
int Py_InspectFlag¶
此 API 僅為向後相容而保留:應改用設定
PyConfig.inspect
,詳見Python 初始化配置。當指令碼作為第一個引數傳遞或使用
-c
選項時,即使sys.stdin
看起來不是終端,在執行指令碼或命令後也會進入互動模式。由
-i
選項和PYTHONINSPECT
環境變數設定。自 3.12 版本起已棄用,將於 3.15 版本中移除。
-
int Py_InteractiveFlag¶
此 API 僅為向後相容而保留:應改用設定
PyConfig.interactive
,詳見Python 初始化配置。由
-i
選項設定。自 3.12 版本起已棄用,將於 3.15 版本中移除。
-
int Py_IsolatedFlag¶
此 API 僅為向後相容而保留:應改用設定
PyConfig.isolated
,詳見Python 初始化配置。在隔離模式下執行 Python。在隔離模式下,
sys.path
既不包含指令碼目錄,也不包含使用者 site-packages 目錄。由
-I
選項設定。在 3.4 版本加入。
自 3.12 版本起已棄用,將於 3.15 版本中移除。
-
int Py_LegacyWindowsFSEncodingFlag¶
此 API 僅為向後相容而保留:應改用設定
PyPreConfig.legacy_windows_fs_encoding
,詳見Python 初始化配置。如果該標誌非零,則對於檔案系統編碼和錯誤處理程式,使用
mbcs
編碼和replace
錯誤處理程式,而不是 UTF-8 編碼和surrogatepass
錯誤處理程式。如果
PYTHONLEGACYWINDOWSFSENCODING
環境變數設定為非空字串,則設定為1
。有關更多詳細資訊,請參閱 PEP 529。
可用性:Windows。
自 3.12 版本起已棄用,將於 3.15 版本中移除。
-
int Py_LegacyWindowsStdioFlag¶
此 API 僅為向後相容而保留:應改用設定
PyConfig.legacy_windows_stdio
,詳見Python 初始化配置。如果該標誌非零,則對於
sys
標準流,使用io.FileIO
而不是io._WindowsConsoleIO
。如果
PYTHONLEGACYWINDOWSSTDIO
環境變數設定為非空字串,則設定為1
。有關更多詳細資訊,請參閱 PEP 528。
可用性:Windows。
自 3.12 版本起已棄用,將於 3.15 版本中移除。
-
int Py_NoSiteFlag¶
此 API 僅為向後相容而保留:應改用設定
PyConfig.site_import
,詳見Python 初始化配置。停用模組
site
的匯入以及它所導致的sys.path
的站點相關操作。即使以後顯式匯入site
,也停用這些操作(如果希望觸發它們,請呼叫site.main()
)。由
-S
選項設定。自 3.12 版本起已棄用,將於 3.15 版本中移除。
-
int Py_NoUserSiteDirectory¶
此 API 僅為向後相容而保留:應改用設定
PyConfig.user_site_directory
,詳見Python 初始化配置。不將
user site-packages directory
新增到sys.path
。由
-s
和-I
選項以及PYTHONNOUSERSITE
環境變數設定。自 3.12 版本起已棄用,將於 3.15 版本中移除。
-
int Py_OptimizeFlag¶
此 API 僅為向後相容而保留:應改用設定
PyConfig.optimization_level
,詳見Python 初始化配置。由
-O
選項和PYTHONOPTIMIZE
環境變數設定。自 3.12 版本起已棄用,將於 3.15 版本中移除。
-
int Py_QuietFlag¶
此 API 僅為向後相容而保留:應改用設定
PyConfig.quiet
,詳見Python 初始化配置。即使在互動模式下也不顯示版權和版本資訊。
由
-q
選項設定。在 3.2 版本加入。
自 3.12 版本起已棄用,將於 3.15 版本中移除。
-
int Py_UnbufferedStdioFlag¶
此 API 僅為向後相容而保留:應改用設定
PyConfig.buffered_stdio
,詳見Python 初始化配置。強制標準輸出和標準錯誤流不帶緩衝。
由
-u
選項和PYTHONUNBUFFERED
環境變數設定。自 3.12 版本起已棄用,將於 3.15 版本中移除。
-
int Py_VerboseFlag¶
此 API 僅為向後相容而保留:應改用設定
PyConfig.verbose
,詳見Python 初始化配置。每次模組初始化時列印一條訊息,顯示其載入位置(檔名或內建模組)。如果大於或等於
2
,則在搜尋模組時,檢查每個檔案時都列印一條訊息。還提供退出時模組清理的資訊。由
-v
選項和PYTHONVERBOSE
環境變數設定。自 3.12 版本起已棄用,將於 3.15 版本中移除。
初始化和終結直譯器¶
-
void Py_Initialize()¶
- 作為 穩定 ABI 的一部分。
初始化 Python 直譯器。在嵌入 Python 的應用程式中,應在使用任何其他 Python/C API 函式之前呼叫此函式;少數例外請參閱Python 初始化之前。
此函式初始化已載入模組的表(
sys.modules
),並建立基本模組builtins
、__main__
和sys
。它還初始化模組搜尋路徑(sys.path
)。它不設定sys.argv
;為此請使用Python 初始化配置 API。第二次呼叫時(未首先呼叫Py_FinalizeEx()
),此函式無效。沒有返回值;如果初始化失敗,則為致命錯誤。使用
Py_InitializeFromConfig()
來自定義Python 初始化配置。備註
在 Windows 上,將控制檯模式從
O_TEXT
更改為O_BINARY
,這也將影響使用 C 執行時對控制檯進行的非 Python 使用。
-
void Py_InitializeEx(int initsigs)¶
- 作為 穩定 ABI 的一部分。
如果 *initsigs* 為
1
,此函式與Py_Initialize()
的作用相同。如果 *initsigs* 為0
,則跳過訊號處理程式的初始化註冊,當 CPython 作為大型應用程式的一部分嵌入時,這可能很有用。
-
PyStatus Py_InitializeFromConfig(const PyConfig *config)¶
根據 使用 PyConfig 初始化 中所述,根據 *config* 配置初始化 Python。
有關預初始化直譯器、填充執行時配置結構以及查詢返回狀態結構的詳細資訊,請參閱Python 初始化配置部分。
-
int Py_IsInitialized()¶
- 作為 穩定 ABI 的一部分。
當 Python 直譯器已初始化時返回真(非零),否則返回假(零)。呼叫
Py_FinalizeEx()
後,此函式返回假,直到再次呼叫Py_Initialize()
。
-
int Py_FinalizeEx()¶
- 自 3.6 版本起成為穩定 ABI 的一部分。
撤銷由
Py_Initialize()
和後續的 Python/C API 函式使用所進行的所有初始化,並銷燬自上次呼叫Py_Initialize()
以來建立但尚未銷燬的所有子直譯器(參見下面的Py_NewInterpreter()
)。第二次呼叫時(未再次呼叫Py_Initialize()
),此函式無效。由於此函式與
Py_Initialize()
相反,因此應在具有相同活動直譯器的同一執行緒中呼叫。這意味著主執行緒和主直譯器。在Py_RunMain()
執行時絕不應呼叫此函式。通常返回值為
0
。如果終結過程中發生錯誤(重新整理緩衝資料),則返回-1
。請注意,Python 將盡最大努力釋放 Python 直譯器分配的所有記憶體。因此,任何 C 擴充套件都應確保在後續呼叫
Py_Initialize()
之前正確清理所有先前分配的 PyObject。否則,可能會引入漏洞和不正確的行為。提供此函式有多種原因。嵌入式應用程式可能希望在不重新啟動應用程式本身的情況下重新啟動 Python。從動態載入庫(或 DLL)載入 Python 直譯器的應用程式可能希望在解除安裝 DLL 之前釋放 Python 分配的所有記憶體。在應用程式中查詢記憶體洩漏時,開發人員可能希望在退出應用程式之前釋放 Python 分配的所有記憶體。
錯誤和注意事項:模組中模組和物件的銷燬是隨機進行的;這可能導致解構函式(
__del__()
方法)在依賴於其他物件(甚至是函式)或模組時失敗。Python 載入的動態載入擴充套件模組不會被解除安裝。Python 直譯器分配的少量記憶體可能不會被釋放(如果發現洩漏,請報告)。物件之間迴圈引用中佔用的記憶體不會被釋放。所有內部字串將被解除分配,無論其引用計數如何。擴充套件模組分配的一些記憶體可能不會被釋放。如果其初始化例程被多次呼叫,一些擴充套件可能無法正常工作;如果應用程式多次呼叫Py_Initialize()
和Py_FinalizeEx()
,就可能發生這種情況。Py_FinalizeEx()
不得在自身內部遞迴呼叫。因此,它不得由任何可能作為直譯器關閉過程的一部分執行的程式碼呼叫,例如atexit
處理程式、物件終結器或在重新整理標準輸出和標準錯誤檔案時可能執行的任何程式碼。引發審計事件
cpython._PySys_ClearAuditHooks
,無引數。在 3.6 版本加入。
-
void Py_Finalize()¶
- 作為 穩定 ABI 的一部分。
這是
Py_FinalizeEx()
的向後相容版本,它忽略返回值。
-
int Py_BytesMain(int argc, char **argv)¶
- 自 3.8 版本以來,作為穩定 ABI 的一部分。
類似於
Py_Main()
,但 *argv* 是一個位元組字串陣列,允許呼叫應用程式將文字解碼步驟委託給 CPython 執行時。在 3.8 版本加入。
-
int Py_Main(int argc, wchar_t **argv)¶
- 作為 穩定 ABI 的一部分。
標準直譯器的主程式,封裝了一個完整的初始化/終結週期,以及用於根據命令列從環境和命令列讀取配置設定然後執行
__main__
的附加行為。這適用於希望支援完整的 CPython 命令列介面,而不僅僅是將 Python 執行時嵌入到更大應用程式中的程式。
*argc* 和 *argv* 引數類似於傳遞給 C 程式
main()
函式的引數,但 *argv* 條目首先使用Py_DecodeLocale()
轉換為wchar_t
。還需要注意的是,引數列表條目可能會被修改為指向傳遞的字串以外的字串(但是,引數列表指向的字串內容不會被修改)。如果引數列表不表示有效的 Python 命令列,則返回值為
2
,否則與Py_RunMain()
相同。就執行時配置部分中記錄的 CPython 執行時配置 API 而言(不考慮錯誤處理),
Py_Main
大致等同於PyConfig config; PyConfig_InitPythonConfig(&config); PyConfig_SetArgv(&config, argc, argv); Py_InitializeFromConfig(&config); PyConfig_Clear(&config); Py_RunMain();
在正常使用中,嵌入式應用程式將**呼叫此函式**,而不是直接呼叫
Py_Initialize()
、Py_InitializeEx()
或Py_InitializeFromConfig()
,並且所有設定都將按本文件其他地方所述應用。如果此函式在之前的執行時初始化 API 呼叫之後呼叫,那麼將更新哪些環境和命令列配置設定將取決於版本(因為它取決於哪些設定在執行時首次初始化後已設定一次後正確支援修改)。
-
int Py_RunMain(void)¶
在完全配置的 CPython 執行時中執行主模組。
執行命令列或配置中指定的命令(
PyConfig.run_command
)、指令碼(PyConfig.run_filename
)或模組(PyConfig.run_module
)。如果這些值都未設定,則使用__main__
模組的全域性名稱空間執行互動式 Python 提示符 (REPL)。如果未設定
PyConfig.inspect
(預設值),則如果直譯器正常退出(即沒有引發異常),返回值為0
;如果未處理SystemExit
,則為其退出狀態;如果發生任何其他未處理的異常,則為1
。如果設定了
PyConfig.inspect
(例如使用-i
選項時),則直譯器退出時不會返回,而是繼續在互動式 Python 提示符 (REPL) 中執行,使用__main__
模組的全域性名稱空間。如果直譯器因異常退出,則該異常會立即在 REPL 會話中引發。然後,函式返回值由 *REPL 會話*終止的方式決定:0
、1
或SystemExit
的狀態,如上所述。此函式在返回之前總是終結 Python 直譯器。
有關始終使用
Py_RunMain()
在隔離模式下執行的自定義 Python 的示例,請參閱Python 配置。
-
int PyUnstable_AtExit(PyInterpreterState *interp, void (*func)(void*), void *data)¶
- 這是一個不穩定 API。它可能會在次要版本中未經警告而更改。
為目標直譯器 *interp* 註冊
atexit
回撥。這類似於Py_AtExit()
,但為回撥提供了顯式直譯器和資料指標。必須存在 *interp* 的附加執行緒狀態。
在 3.13 版本加入。
程序範圍引數¶
-
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 版本起已棄用,將於 3.15 版本中移除。
-
wchar_t *Py_GetProgramName()¶
- 作為 穩定 ABI 的一部分。
返回使用
PyConfig.program_name
設定的程式名稱,或預設值。返回的字串指向靜態儲存;呼叫者不應修改其值。此函式不應在
Py_Initialize()
之前呼叫,否則返回NULL
。3.10 版本中的變化: 現在如果在
Py_Initialize()
之前呼叫,則返回NULL
。自 3.13 版本起已棄用,將於 3.15 版本中移除: 請改用
PyConfig_Get("executable")
(sys.executable
)。
-
wchar_t *Py_GetPrefix()¶
- 作為 穩定 ABI 的一部分。
返回已安裝的平臺無關檔案的*字首*。這是透過從使用
PyConfig.program_name
設定的程式名稱和一些環境變數得出的複雜規則生成的;例如,如果程式名稱是'/usr/local/bin/python'
,則字首是'/usr/local'
。返回的字串指向靜態儲存;呼叫者不應修改其值。這對應於頂級Makefile
中的 prefix 變數以及構建時 configure 指令碼的--prefix
引數。該值作為sys.base_prefix
可用於 Python 程式碼。它僅在 Unix 上有用。另請參閱下一個函式。此函式不應在
Py_Initialize()
之前呼叫,否則返回NULL
。3.10 版本中的變化: 現在如果在
Py_Initialize()
之前呼叫,則返回NULL
。自 3.13 版本起已棄用,將於 3.15 版本中移除: 請改用
PyConfig_Get("base_prefix")
(sys.base_prefix
)。如果需要處理虛擬環境,請使用PyConfig_Get("prefix")
(sys.prefix
)。
-
wchar_t *Py_GetExecPrefix()¶
- 作為 穩定 ABI 的一部分。
返回已安裝的平臺*相關*檔案的*執行字首*。這是透過從使用
PyConfig.program_name
設定的程式名稱和一些環境變數得出的複雜規則生成的;例如,如果程式名稱是'/usr/local/bin/python'
,則執行字首是'/usr/local'
。返回的字串指向靜態儲存;呼叫者不應修改其值。這對應於頂級Makefile
中的 exec_prefix 變數以及構建時 configure 指令碼的--exec-prefix
引數。該值作為sys.base_exec_prefix
可用於 Python 程式碼。它僅在 Unix 上有用。背景:當平臺相關檔案(例如可執行檔案和共享庫)安裝在不同的目錄樹中時,執行字首與字首不同。在典型的安裝中,平臺相關檔案可能安裝在
/usr/local/plat
子樹中,而平臺無關檔案可能安裝在/usr/local
中。一般來說,一個平臺是硬體和軟體系列的組合,例如,執行 Solaris 2.x 作業系統的 Sparc 機器被認為是同一平臺,但執行 Solaris 2.x 的 Intel 機器是另一個平臺,執行 Linux 的 Intel 機器又是另一個平臺。同一作業系統的不同主要版本通常也構成不同的平臺。非 Unix 作業系統是另一回事;這些系統上的安裝策略如此不同,以字首和執行字首都毫無意義,並設定為一個空字串。請注意,編譯後的 Python 位元組碼檔案是平臺無關的(但與編譯它們的 Python 版本並非無關!)。
系統管理員將知道如何配置 mount 或 automount 程式,以在平臺之間共享
/usr/local
,同時使/usr/local/plat
成為每個平臺不同的檔案系統。此函式不應在
Py_Initialize()
之前呼叫,否則返回NULL
。3.10 版本中的變化: 現在如果在
Py_Initialize()
之前呼叫,則返回NULL
。自 3.13 版本起已棄用,將於 3.15 版本中移除: 請改用
PyConfig_Get("base_exec_prefix")
(sys.base_exec_prefix
)。如果需要處理虛擬環境,請使用PyConfig_Get("exec_prefix")
(sys.exec_prefix
)。
-
wchar_t *Py_GetProgramFullPath()¶
- 作為 穩定 ABI 的一部分。
返回 Python 可執行檔案的完整程式名稱;這是從程式名稱(由
PyConfig.program_name
設定)推導預設模組搜尋路徑的附帶結果計算得出的。返回的字串指向靜態儲存;呼叫者不應修改其值。該值作為sys.executable
可用於 Python 程式碼。此函式不應在
Py_Initialize()
之前呼叫,否則返回NULL
。3.10 版本中的變化: 現在如果在
Py_Initialize()
之前呼叫,則返回NULL
。自 3.13 版本起已棄用,將於 3.15 版本中移除: 請改用
PyConfig_Get("executable")
(sys.executable
)。
-
wchar_t *Py_GetPath()¶
- 作為 穩定 ABI 的一部分。
返回預設模組搜尋路徑;這是從程式名稱(由
PyConfig.program_name
設定)和一些環境變數計算得出的。返回的字串由一系列目錄名組成,這些目錄名由平臺相關的分隔符字元分隔。分隔符字元在 Unix 和 macOS 上是':'
,在 Windows 上是';'
。返回的字串指向靜態儲存;呼叫者不應修改其值。列表sys.path
在直譯器啟動時使用此值初始化;以後可以(並且通常會)修改它以更改載入模組的搜尋路徑。此函式不應在
Py_Initialize()
之前呼叫,否則返回NULL
。3.10 版本中的變化: 現在如果在
Py_Initialize()
之前呼叫,則返回NULL
。自 3.13 版本起已棄用,將於 3.15 版本中移除: 請改用
PyConfig_Get("module_search_paths")
(sys.path
)。
-
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'
。返回的字串指向靜態儲存;呼叫者不應修改其值。該值作為sys.platform
可用於 Python 程式碼。
-
const char *Py_GetCopyright()¶
- 作為 穩定 ABI 的一部分。
返回當前 Python 版本的官方版權字串,例如
'Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam'
返回的字串指向靜態儲存;呼叫者不應修改其值。該值作為
sys.copyright
可用於 Python 程式碼。
-
const char *Py_GetCompiler()¶
- 作為 穩定 ABI 的一部分。
返回用於構建當前 Python 版本的編譯器的指示,用方括號括起來,例如
"[GCC 2.7.2.2]"
返回的字串指向靜態儲存;呼叫者不應修改其值。該值作為
sys.version
變數的一部分可用於 Python 程式碼。
-
const char *Py_GetBuildInfo()¶
- 作為 穩定 ABI 的一部分。
返回有關當前 Python 直譯器例項的序列號以及構建日期和時間的資訊,例如
"#67, Aug 1 1997, 22:34:28"
返回的字串指向靜態儲存;呼叫者不應修改其值。該值作為
sys.version
變數的一部分可用於 Python 程式碼。
-
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_t* 字串。另請參閱Python 初始化配置的
PyConfig.orig_argv
和PyConfig.argv
成員。備註
建議除了執行單個指令碼之外的其他目的嵌入 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 版本中新增。
自 3.11 版本起已棄用,將於 3.15 版本中移除。
-
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_t* 字串。另請參閱Python 初始化配置的
PyConfig.orig_argv
和PyConfig.argv
成員。3.4 版本中已更改: updatepath 值取決於
-I
。自 3.11 版本起已棄用,將於 3.15 版本中移除。
-
void Py_SetPythonHome(const wchar_t *home)¶
- 作為 穩定 ABI 的一部分。
此 API 保留用於向後相容:應使用設定
PyConfig.home
,詳見 Python 初始化配置。設定預設的“home”目錄,即標準 Python 庫的位置。有關引數字串的含義,請參見
PYTHONHOME
。該引數應指向靜態儲存中的一個以零結尾的字串,其內容在程式執行期間不會改變。Python 直譯器中的任何程式碼都不會改變此儲存的內容。
使用
Py_DecodeLocale()
解碼位元組字串以獲取 wchar_t* 字串。自 3.11 版本起已棄用,將於 3.15 版本中移除。
-
wchar_t *Py_GetPythonHome()¶
- 作為 穩定 ABI 的一部分。
返回預設的“home”,即由
PyConfig.home
設定的值,或者如果設定了PYTHONHOME
環境變數,則返回其值。此函式不應在
Py_Initialize()
之前呼叫,否則返回NULL
。3.10 版本中的變化: 現在如果在
Py_Initialize()
之前呼叫,則返回NULL
。自 3.13 版本棄用,將於 3.15 版本移除: 請使用
PyConfig_Get("home")
或PYTHONHOME
環境變數。
執行緒狀態和全域性直譯器鎖¶
除非是在自由執行緒構建的CPython上,Python直譯器並非完全執行緒安全。為了支援多執行緒Python程式,存在一個全域性鎖,稱為全域性直譯器鎖或GIL,當前執行緒在安全訪問Python物件之前必須持有它。如果沒有這個鎖,即使是最簡單的操作也可能在多執行緒程式中引起問題:例如,當兩個執行緒同時遞增同一物件的引用計數時,引用計數可能只增加一次而不是兩次。
因此,存在一條規則,即只有持有 GIL 的執行緒才能操作 Python 物件或呼叫 Python/C API 函式。為了模擬執行的併發性,直譯器會定期嘗試切換執行緒(參見 sys.setswitchinterval()
)。在可能阻塞的 I/O 操作(如讀寫檔案)周圍也會釋放鎖,以便其他 Python 執行緒在此期間執行。
Python 直譯器將一些執行緒特定的記賬資訊儲存在一個名為 PyThreadState
的資料結構中,這被稱為執行緒狀態。每個作業系統執行緒都有一個指向 PyThreadState
的執行緒本地指標;由該指標引用的執行緒狀態被認為是已附加的。
一個執行緒一次只能有一個已附加執行緒狀態。一個已附加執行緒狀態通常與持有GIL類似,但自由執行緒構建除外。在啟用GIL的構建中,附加執行緒狀態將阻塞,直到可以獲取GIL。然而,即使在停用GIL的構建中,仍然需要有已附加執行緒狀態才能呼叫大部分C API。
一般來說,在使用 Python 的 C API 時,總會有一個已附加執行緒狀態。只有在某些特定情況下(例如在 Py_BEGIN_ALLOW_THREADS
塊中),執行緒才不會有已附加執行緒狀態。如果不確定,請檢查 PyThreadState_GetUnchecked()
是否返回 NULL
。
從擴充套件程式碼中分離執行緒狀態¶
大多數操作執行緒狀態的擴充套件程式碼具有以下簡單結構
Save the thread state in a local variable.
... Do some blocking I/O operation ...
Restore the thread state from the local variable.
這很常見,因此有一對宏可以簡化它
Py_BEGIN_ALLOW_THREADS
... Do some blocking I/O operation ...
Py_END_ALLOW_THREADS
Py_BEGIN_ALLOW_THREADS
宏開啟一個新塊並宣告一個隱藏的區域性變數;Py_END_ALLOW_THREADS
宏關閉該塊。
上面的程式碼塊展開為以下程式碼
PyThreadState *_save;
_save = PyEval_SaveThread();
... Do some blocking I/O operation ...
PyEval_RestoreThread(_save);
這些函式的工作方式如下
已附加執行緒狀態為整個直譯器持有GIL。當分離已附加執行緒狀態時,GIL被釋放,允許其他執行緒將執行緒狀態附加到自己的執行緒,從而獲取GIL並開始執行。指向先前已附加執行緒狀態的指標儲存為區域性變數。當到達Py_END_ALLOW_THREADS
時,之前已附加的執行緒狀態被傳遞給PyEval_RestoreThread()
。此函式將阻塞,直到另一個執行緒釋放其執行緒狀態,從而允許舊的執行緒狀態重新附加,並且可以再次呼叫C API。
對於自由執行緒構建,GIL通常不在考慮範圍內,但對於阻塞I/O和長時間操作仍然需要分離執行緒狀態。不同之處在於,執行緒不必等待GIL釋放即可附加其執行緒狀態,從而實現真正的多核並行性。
非 Python 建立的執行緒¶
當使用專門的 Python API(例如 threading
模組)建立執行緒時,執行緒狀態會自動與之關聯,因此上面顯示的程式碼是正確的。然而,當從 C 建立執行緒時(例如由具有自己執行緒管理的第三方庫建立),它們不持有 GIL,因為它們沒有已附加執行緒狀態。
如果您需要從這些執行緒呼叫 Python 程式碼(通常這將是上述第三方庫提供的回撥 API 的一部分),則在開始使用 Python/C API 之前,您必須首先透過建立已附加執行緒狀態來向直譯器註冊這些執行緒。完成後,您應該分離執行緒狀態,最後將其釋放。
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 是不受支援的。這是因為 PyGILState_Ensure()
和類似函式預設附加主直譯器的執行緒狀態,這意味著執行緒無法安全地與呼叫子直譯器互動。
在非 Python 執行緒中支援子直譯器¶
如果您想在非 Python 建立的執行緒中支援子直譯器,則必須使用 PyThreadState_*
API 而不是傳統的 PyGILState_*
API。
特別是,您必須儲存來自呼叫函式的直譯器狀態並將其傳遞給 PyThreadState_New()
,這將確保執行緒狀態指向正確的直譯器
/* The return value of PyInterpreterState_Get() from the
function that created this thread. */
PyInterpreterState *interp = ThreadData->interp;
PyThreadState *tstate = PyThreadState_New(interp);
PyThreadState_Swap(tstate);
/* GIL of the subinterpreter is now held.
Perform Python actions here. */
result = CallSomeFunction();
/* evaluate result or handle exception */
/* Destroy the thread state. No Python API allowed beyond this point. */
PyThreadState_Clear(tstate);
PyThreadState_DeleteCurrent();
關於 fork() 的注意事項¶
關於執行緒,另一個需要注意的重要事項是它們在 C fork()
呼叫面前的行為。在大多數支援 fork()
的系統上,程序 fork 後只剩下發出 fork 的執行緒。這會對鎖的處理方式和 CPython 執行時中所有儲存的狀態產生具體影響。
只剩下“當前”執行緒的事實意味著其他執行緒持有的任何鎖將永遠不會被釋放。Python 針對 os.fork()
解決了這個問題,它在 fork 之前獲取內部使用的鎖,並在之後釋放它們。此外,它會重置子程序中的任何 Lock 物件。在擴充套件或嵌入 Python 時,無法通知 Python 在 fork 之前需要獲取或在 fork 之後需要重置的額外(非 Python)鎖。需要使用 pthread_atfork()
等作業系統設施來實現相同的功能。此外,在擴充套件或嵌入 Python 時,直接呼叫 fork()
而不是透過 os.fork()
(並返回到或呼叫 Python)可能會由於 Python 的內部鎖之一被 fork 後已失效的執行緒持有而導致死鎖。PyOS_AfterFork_Child()
嘗試重置必要的鎖,但並非總能成功。
所有其他執行緒都消失的事實也意味著 CPython 的執行時狀態必須得到適當清理,而 os.fork()
確實做到了這一點。這意味著要完成屬於當前直譯器的所有其他 PyThreadState
物件和所有其他 PyInterpreterState
物件。鑑於此以及 “主”直譯器 的特殊性,fork()
應該只在該直譯器的“主”執行緒中呼叫,即 CPython 全域性執行時最初初始化的地方。唯一的例外是如果緊接著將呼叫 exec()
。
關於執行時終結的注意事項¶
在直譯器關閉的後期階段,嘗試等待非守護執行緒退出(儘管這可能會被 KeyboardInterrupt
中斷)並執行 atexit
函式之後,執行時被標記為*正在終結*:Py_IsFinalizing()
和 sys.is_finalizing()
返回 true。此時,只有啟動終結的*終結執行緒*(通常是主執行緒)才允許獲取 GIL。
如果任何執行緒(終結執行緒除外)在終結期間嘗試顯式或隱式地附加執行緒狀態,該執行緒將進入**永久阻塞狀態**,直到程式退出。在大多數情況下,這是無害的,但如果終結的後續階段嘗試獲取被阻塞執行緒擁有的鎖,或者以其他方式等待被阻塞執行緒,則可能導致死鎖。
糟糕?是的。這可以防止在 CPython 3.13 及更早版本中強制退出此類執行緒時,呼叫堆疊中更上層的隨機崩潰和/或意外跳過的 C++ 終結。CPython 執行時執行緒狀態 C API 在執行緒狀態附加時從未有過任何錯誤報告或處理預期,這本可以允許優雅地退出這種情況。改變這一點將需要新的穩定 C API 並重寫 CPython 生態系統中大多數 C 程式碼以使用這些 API 並進行錯誤處理。
高階 API¶
這些是編寫 C 擴充套件程式碼或嵌入 Python 直譯器時最常用的型別和函式
-
type PyInterpreterState¶
- 受限 API 的一部分(作為不透明結構體)。
此資料結構表示多個協作執行緒共享的狀態。屬於同一直譯器的執行緒共享其模組管理和一些其他內部項。此結構中沒有公共成員。
屬於不同直譯器的執行緒最初不共享任何內容,除了程序狀態,例如可用記憶體、開啟的檔案描述符等。全域性直譯器鎖也由所有執行緒共享,無論它們屬於哪個直譯器。
3.12 版本中已更改: PEP 684 引入了 每直譯器 GIL 的可能性。請參閱
Py_NewInterpreterFromConfig()
。
-
type PyThreadState¶
- 受限 API 的一部分(作為不透明結構體)。
此資料結構表示單個執行緒的狀態。唯一的公共資料成員是
-
PyInterpreterState *interp¶
此執行緒的直譯器狀態。
-
PyInterpreterState *interp¶
-
void PyEval_InitThreads()¶
- 作為 穩定 ABI 的一部分。
已棄用,不起作用。
在Python 3.6及更早版本中,此函式在GIL不存在時建立GIL。
3.9 版本中已更改: 該函式現在什麼也不做。
3.7 版本中已更改: 此函式現在由
Py_Initialize()
呼叫,因此您不再需要自己呼叫它。3.2 版本中已更改: 此函式不能再在
Py_Initialize()
之前呼叫。自 3.9 版本棄用。
-
PyThreadState *PyEval_SaveThread()¶
- 作為 穩定 ABI 的一部分。
-
void PyEval_RestoreThread(PyThreadState *tstate)¶
- 作為 穩定 ABI 的一部分。
將已附加執行緒狀態設定為 tstate。傳入的執行緒狀態**不應該**是已附加的,否則會導致死鎖。tstate 將在返回時被附加。
備註
在執行時正在終結時從執行緒呼叫此函式將使該執行緒掛起,直到程式退出,即使該執行緒不是由 Python 建立的。有關更多詳細資訊,請參閱 關於執行時終結的注意事項。
3.14 版本中已更改: 如果在直譯器正在終結時呼叫,則會掛起當前執行緒,而不是終止它。
-
PyThreadState *PyThreadState_Get()¶
- 作為 穩定 ABI 的一部分。
返回已附加執行緒狀態。如果執行緒沒有已附加執行緒狀態(例如在
Py_BEGIN_ALLOW_THREADS
塊內部時),則會發出致命錯誤(以便呼叫者不必檢查NULL
)。
-
PyThreadState *PyThreadState_GetUnchecked()¶
與
PyThreadState_Get()
類似,但如果為 NULL 則不會因致命錯誤而終止程序。呼叫者負責檢查結果是否為 NULL。3.13 版本中新增: 在 Python 3.5 到 3.12 中,該函式是私有的,被稱為
_PyThreadState_UncheckedGet()
。
-
PyThreadState *PyThreadState_Swap(PyThreadState *tstate)¶
- 作為 穩定 ABI 的一部分。
將已附加執行緒狀態設定為 tstate,並返回呼叫前已附加的執行緒狀態。
此函式在沒有已附加執行緒狀態的情況下呼叫是安全的;它只會返回
NULL
,表示沒有先前的執行緒狀態。備註
與
PyGILState_Ensure()
類似,如果執行時正在終結,此函式將掛起執行緒。
以下函式使用執行緒本地儲存,與子直譯器不相容
-
type PyGILState_STATE¶
- 作為 穩定 ABI 的一部分。
PyGILState_Ensure()
返回並傳遞給PyGILState_Release()
的值的型別。-
enumerator PyGILState_LOCKED¶
呼叫
PyGILState_Ensure()
時 GIL 已被持有。
-
enumerator PyGILState_UNLOCKED¶
呼叫
PyGILState_Ensure()
時 GIL 未被持有。
-
enumerator PyGILState_LOCKED¶
-
PyGILState_STATE PyGILState_Ensure()¶
- 作為 穩定 ABI 的一部分。
確保當前執行緒已準備好呼叫 Python C API,無論 Python 當前狀態或已附加執行緒狀態如何。只要每次呼叫都與一次
PyGILState_Release()
呼叫匹配,執行緒就可以隨意多次呼叫此函式。通常,在PyGILState_Ensure()
和PyGILState_Release()
呼叫之間可以使用其他執行緒相關 API,只要在 Release() 之前將執行緒狀態恢復到其先前狀態即可。例如,Py_BEGIN_ALLOW_THREADS
和Py_END_ALLOW_THREADS
宏的正常使用是可接受的。返回值是呼叫
PyGILState_Ensure()
時已附加執行緒狀態的不透明“控制代碼”,並且必須傳遞給PyGILState_Release()
以確保 Python 保持在相同的狀態。儘管允許遞迴呼叫,但這些控制代碼**不能**共享——每次對PyGILState_Ensure()
的唯一呼叫都必須為其對PyGILState_Release()
的呼叫儲存控制代碼。函式返回時,將有一個已附加執行緒狀態,並且執行緒將能夠呼叫任意 Python 程式碼。失敗是致命錯誤。
警告
在執行時正在終結時呼叫此函式是不安全的。這樣做要麼會使執行緒掛起直到程式結束,要麼在極少數情況下完全崩潰直譯器。有關更多詳細資訊,請參閱 關於執行時終結的注意事項。
3.14 版本中已更改: 如果在直譯器正在終結時呼叫,則會掛起當前執行緒,而不是終止它。
-
void PyGILState_Release(PyGILState_STATE)¶
- 作為 穩定 ABI 的一部分。
釋放先前獲取的任何資源。在此呼叫之後,Python 的狀態將與相應
PyGILState_Ensure()
呼叫之前的狀態相同(但通常此狀態對呼叫者未知,因此使用 GILState API)。每次呼叫
PyGILState_Ensure()
都必須與同一執行緒上的PyGILState_Release()
呼叫匹配。
-
PyThreadState *PyGILState_GetThisThreadState()¶
- 作為 穩定 ABI 的一部分。
獲取此執行緒的已附加執行緒狀態。如果當前執行緒未使用任何 GILState API,則可能返回
NULL
。請注意,主執行緒始終具有此類執行緒狀態,即使主執行緒上未進行自動執行緒狀態呼叫。這主要是輔助/診斷函式。備註
即使執行緒狀態已分離,此函式也可能返回非
NULL
。在大多數情況下,首選PyThreadState_Get()
或PyThreadState_GetUnchecked()
。
-
int PyGILState_Check()¶
如果當前執行緒持有 GIL,則返回
1
,否則返回0
。此函式可由任何執行緒在任何時間呼叫。只有當它的執行緒狀態已透過PyGILState_Ensure()
初始化後,它才會返回1
。這主要是一個輔助/診斷函式。例如,在回撥上下文或記憶體分配函式中,瞭解 GIL 已鎖定可以允許呼叫者執行敏感操作或以其他方式以不同方式行事時,它可能很有用。備註
如果當前 Python 程序曾經建立過子直譯器,此函式將*始終*返回
1
。在大多數情況下,首選PyThreadState_GetUnchecked()
。在 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 的一部分。
重置執行緒狀態物件中的所有資訊。tstate 必須是已附加的
3.9 版本中已更改: 此函式現在呼叫
PyThreadState.on_delete
回撥。此前,此操作發生在PyThreadState_Delete()
中。3.13 版本中已更改:
PyThreadState.on_delete
回撥已移除。
-
void PyThreadState_Delete(PyThreadState *tstate)¶
- 作為 穩定 ABI 的一部分。
銷燬一個執行緒狀態物件。tstate 不應附加到任何執行緒。tstate 必須已透過之前呼叫
PyThreadState_Clear()
進行重置。
-
void PyThreadState_DeleteCurrent(void)¶
分離已附加執行緒狀態(該狀態必須已透過先前呼叫
PyThreadState_Clear()
重置),然後銷燬它。
-
PyFrameObject *PyThreadState_GetFrame(PyThreadState *tstate)¶
- 自 3.10 版本以來,作為 穩定 ABI 的一部分。
獲取 Python 執行緒狀態 tstate 的當前幀。
返回一個強引用。如果沒有幀正在執行,則返回
NULL
。另請參閱
PyEval_GetFrame()
。tstate 不得為
NULL
,並且必須已附加。在 3.9 版本中新增。
-
uint64_t PyThreadState_GetID(PyThreadState *tstate)¶
- 自 3.10 版本以來,作為 穩定 ABI 的一部分。
獲取 Python 執行緒狀態 tstate 的唯一執行緒狀態識別符號。
tstate 不得為
NULL
,並且必須已附加。在 3.9 版本中新增。
-
PyInterpreterState *PyThreadState_GetInterpreter(PyThreadState *tstate)¶
- 自 3.10 版本以來,作為 穩定 ABI 的一部分。
獲取 Python 執行緒狀態 tstate 的直譯器。
tstate 不得為
NULL
,並且必須已附加。在 3.9 版本中新增。
-
void PyThreadState_EnterTracing(PyThreadState *tstate)¶
暫停 Python 執行緒狀態 tstate 中的跟蹤和分析。
使用
PyThreadState_LeaveTracing()
函式恢復它們。在 3.11 版本中新增。
-
void PyThreadState_LeaveTracing(PyThreadState *tstate)¶
恢復由
PyThreadState_EnterTracing()
函式暫停的 Python 執行緒狀態 tstate 中的跟蹤和分析。另請參閱
PyEval_SetTrace()
和PyEval_SetProfile()
函式。在 3.11 版本中新增。
-
PyInterpreterState *PyInterpreterState_Get(void)¶
- 自 3.9 版本以來成為 穩定 ABI 的一部分。
獲取當前直譯器。
如果沒有已附加執行緒狀態,則會發出致命錯誤。它不能返回 NULL。
在 3.9 版本中新增。
-
int64_t PyInterpreterState_GetID(PyInterpreterState *interp)¶
- 自 3.7 版本起成為 穩定ABI 的一部分。
返回直譯器的唯一 ID。如果在此過程中發生任何錯誤,則返回
-1
並設定錯誤。呼叫者必須具有 已附加的執行緒狀態。
在 3.7 版本加入。
-
PyObject *PyInterpreterState_GetDict(PyInterpreterState *interp)¶
- 返回值: 借用引用。 自 3.8 版本起成為 穩定 ABI 的一部分。
返回一個字典,其中可以儲存特定於直譯器的資料。如果此函式返回
NULL
,則未引發異常,呼叫者應假定沒有可用的特定於直譯器的字典。這不能替代
PyModule_GetState()
,擴充套件應使用它來儲存特定於直譯器的狀態資訊。返回的字典是從直譯器借用的,在直譯器關閉之前有效。
在 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 “向 CPython 新增幀評估 API”。
在 3.9 版本中新增。
-
void _PyInterpreterState_SetEvalFrameFunc(PyInterpreterState *interp, _PyFrameEvalFunction eval_frame)¶
設定幀評估函式。
請參閱 PEP 523 “向 CPython 新增幀評估 API”。
在 3.9 版本中新增。
-
PyObject *PyThreadState_GetDict()¶
- 返回值: 借用引用。 穩定ABI 的一部分。
返回一個字典,擴充套件可以在其中儲存執行緒特定的狀態資訊。每個擴充套件都應該使用一個唯一的鍵來在字典中儲存狀態。在沒有執行緒狀態附加的情況下呼叫此函式是允許的。如果此函式返回
NULL
,則沒有引發異常,呼叫者應假定沒有執行緒狀態附加。
-
int PyThreadState_SetAsyncExc(unsigned long id, PyObject *exc)¶
- 作為 穩定 ABI 的一部分。
在一個執行緒中非同步引發異常。id 引數是目標執行緒的執行緒 ID;exc 是要引發的異常物件。此函式不會竊取 exc 的任何引用。為防止誤用,您必須編寫自己的 C 擴充套件來呼叫此函式。必須在附加執行緒狀態下呼叫。返回已修改的執行緒狀態數量;這通常是一個,但如果找不到執行緒 ID,則為零。如果 exc 為
NULL
,則清除執行緒的待處理異常(如果有)。這不會引發任何異常。3.7 版更改: id 引數的型別從 long 更改為 unsigned long。
-
void PyEval_AcquireThread(PyThreadState *tstate)¶
- 作為 穩定 ABI 的一部分。
附加 tstate 到當前執行緒,該執行緒不得為
NULL
或已附加。呼叫執行緒不得已有附加執行緒狀態。
備註
在執行時正在終結時從執行緒呼叫此函式將使該執行緒掛起,直到程式退出,即使該執行緒不是由 Python 建立的。有關更多詳細資訊,請參閱 關於執行時終結的注意事項。
3.8 版更改: 已更新,以與
PyEval_RestoreThread()
、Py_END_ALLOW_THREADS()
和PyGILState_Ensure()
保持一致,並在直譯器正在終止時呼叫時終止當前執行緒。3.14 版本中已更改: 如果在直譯器正在終結時呼叫,則會掛起當前執行緒,而不是終止它。
PyEval_RestoreThread()
是一個更高級別的函式,始終可用(即使線上程尚未初始化時)。
-
void PyEval_ReleaseThread(PyThreadState *tstate)¶
- 作為 穩定 ABI 的一部分。
分離附加執行緒狀態。tstate 引數不得為
NULL
,僅用於檢查它是否表示附加執行緒狀態 — 如果不是,則報告致命錯誤。PyEval_SaveThread()
是一個更高級別的函式,始終可用(即使線上程尚未初始化時)。
子直譯器支援¶
儘管在大多數使用中,您只嵌入一個 Python 直譯器,但在某些情況下,您需要在同一個程序中,甚至可能在同一個執行緒中建立幾個獨立的直譯器。子直譯器允許您這樣做。
“主”直譯器是執行時初始化時建立的第一個直譯器。它通常是程序中唯一的 Python 直譯器。與子直譯器不同,主直譯器具有獨特的程序全域性職責,如訊號處理。它還負責執行時初始化期間的執行,並且通常是執行時終結期間的活動直譯器。PyInterpreterState_Main()
函式返回其狀態的指標。
您可以使用 PyThreadState_Swap()
函式在子直譯器之間切換。您可以使用以下函式建立和銷燬它們
-
type PyInterpreterConfig¶
包含大多數用於配置子直譯器的引數的結構。其值僅在
Py_NewInterpreterFromConfig()
中使用,並且從未被執行時修改。3.12 新版功能.
結構欄位
-
int use_main_obmalloc¶
如果此值為
0
,則子直譯器將使用其自己的“物件”分配器狀態。否則,它將使用(共享)主直譯器的分配器狀態。如果此值為
0
,則check_multi_interp_extensions
必須為1
(非零)。如果此值為1
,則gil
不得為PyInterpreterConfig_OWN_GIL
。
-
int allow_fork¶
如果此值為
0
,則執行時將不支援在子直譯器當前處於活動狀態的任何執行緒中派生程序。否則,派生不受限制。請注意,當禁止派生時,
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 建立,則呼叫直譯器的附加執行緒狀態將被分離。當函式返回時,新直譯器的執行緒狀態將附加到當前執行緒,並且上一個直譯器的附加執行緒狀態將保持分離。
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 = NULL; PyStatus status = Py_NewInterpreterFromConfig(&tstate, &config); if (PyStatus_Exception(status)) { Py_ExitStatusException(status); }
請注意,配置僅短暫使用,不會被修改。在初始化期間,配置的值會轉換為各種
PyInterpreterState
值。配置的只讀副本可以內部儲存在PyInterpreterState
上。擴充套件模組在(子)直譯器之間共享如下
對於使用多階段初始化的模組,例如
PyModule_FromDefAndSpec()
,會為每個直譯器建立並初始化一個單獨的模組物件。只有 C 級別的靜態和全域性變數在這些模組物件之間共享。對於使用單階段初始化的模組,例如
PyModule_Create()
,當某個擴充套件首次匯入時,它會正常初始化,並將其模組字典的(淺)副本儲存起來。當同一擴充套件被另一個(子)直譯器匯入時,會初始化一個新模組並用此副本的內容填充;擴充套件的init
函式不會被呼叫。因此,模組字典中的物件最終會在(子)直譯器之間共享,這可能會導致意外行為(請參閱下面的錯誤和注意事項)。請注意,這與在透過呼叫
Py_FinalizeEx()
和Py_Initialize()
完全重新初始化直譯器後匯入擴充套件時發生的情況不同;在這種情況下,擴充套件的initmodule
函式會再次呼叫。與多階段初始化一樣,這意味著只有 C 級別的靜態和全域性變數在這些模組之間共享。
-
PyThreadState *Py_NewInterpreter(void)¶
- 作為 穩定 ABI 的一部分。
建立一個新的子直譯器。這本質上只是
Py_NewInterpreterFromConfig()
的一個包裝,其配置保留了現有行為。結果是一個非隔離的子直譯器,它共享主直譯器的 GIL,允許 fork/exec,允許守護執行緒,並允許單階段 init 模組。
-
void Py_EndInterpreter(PyThreadState *tstate)¶
- 作為 穩定 ABI 的一部分。
銷燬由給定執行緒狀態表示的(子)直譯器。給定的執行緒狀態必須是附加的。當呼叫返回時,將沒有附加執行緒狀態。與此直譯器關聯的所有執行緒狀態都將被銷燬。
Py_FinalizeEx()
將銷燬所有尚未明確銷燬的子直譯器。
每個直譯器一個 GIL¶
使用 Py_NewInterpreterFromConfig()
,您可以建立一個與其他直譯器完全隔離的子直譯器,包括擁有自己的 GIL。這種隔離的最重要好處是,此類直譯器可以執行 Python 程式碼,而不會被其他直譯器阻塞,也不會阻塞其他任何直譯器。因此,單個 Python 程序在執行 Python 程式碼時可以真正利用多個 CPU 核心。這種隔離還鼓勵了一種不同於僅使用執行緒的併發方法。(參見PEP 554 和PEP 684。)
使用隔離直譯器需要警惕維護這種隔離。這意味著,在沒有執行緒安全保證的情況下,不要共享任何物件或可變狀態。即使是那些通常不可變的物件(例如 None
,(1, 5)
),通常也不能共享,因為引用計數的問題。一個簡單但效率較低的解決方法是圍繞所有使用某個狀態(或物件)的操作使用全域性鎖。或者,實際上不可變的物件(如整數或字串)可以使其不朽,從而使其在引用計數方面安全。事實上,對於內建的單例、小整數和許多其他內建物件,已經這樣做了。
如果你保持隔離,那麼你將能夠訪問適當的多核計算,而無需自由執行緒帶來的複雜性。未能保持隔離將使你面臨自由執行緒的全部後果,包括競爭條件和難以除錯的崩潰。
除此之外,使用多個隔離直譯器的主要挑戰之一是如何在它們之間安全(不破壞隔離)且高效地通訊。執行時和標準庫尚未提供任何標準方法來解決這個問題。未來的標準庫模組將有助於減輕保持隔離的工作量,並提供有效的工具來在直譯器之間通訊(和共享)資料。
3.12 新版功能.
錯誤和注意事項¶
由於子直譯器(和主直譯器)是同一程序的一部分,它們之間的隔離並不完美——例如,使用 os.close()
等低階檔案操作,它們可以(意外或惡意地)影響彼此的開啟檔案。由於擴充套件在(子)直譯器之間共享的方式,某些擴充套件可能無法正常工作;這在使用單階段初始化或(靜態)全域性變數時尤其可能。可以將一個子直譯器中建立的物件插入到另一個(子)直譯器的名稱空間中;如果可能,應避免這種情況。
應特別注意避免在子直譯器之間共享使用者定義的函式、方法、例項或類,因為這些物件執行的匯入操作可能會影響錯誤的(子)直譯器的已載入模組字典。同樣重要的是要避免共享可訪問上述物件的物件。
另請注意,將此功能與 PyGILState_*
API 結合使用是微妙的,因為這些 API 假定 Python 執行緒狀態和作業系統級別執行緒之間存在雙射關係,而子直譯器的存在打破了這一假設。強烈建議您不要在一對匹配的 PyGILState_Ensure()
和 PyGILState_Release()
呼叫之間切換子直譯器。此外,使用這些 API 允許從非 Python 建立的執行緒呼叫 Python 程式碼的擴充套件(例如 ctypes
)在使用子直譯器時可能會損壞。
非同步通知¶
提供了一種機制,用於向主直譯器執行緒傳送非同步通知。這些通知的形式是函式指標和 void 指標引數。
-
int Py_AddPendingCall(int (*func)(void*), void *arg)¶
- 作為 穩定 ABI 的一部分。
安排一個函式從主直譯器執行緒呼叫。成功時,返回
0
,並且 func 被排隊等待在主執行緒中呼叫。失敗時,返回-1
,不設定任何異常。成功排隊後,func 將最終從主直譯器執行緒呼叫,並帶引數 arg。它將相對於正常執行的 Python 程式碼非同步呼叫,但需滿足以下兩個條件
func 必須在成功時返回
0
,或在失敗時設定異常並返回-1
。func 不會被遞迴地中斷以執行另一個非同步通知,但如果執行緒狀態分離,它仍然可以被中斷以切換執行緒。此函式不需要附加執行緒狀態。然而,要在子直譯器中呼叫此函式,呼叫者必須具有附加執行緒狀態。否則,函式 func 可能會被安排從錯誤的直譯器呼叫。
警告
這是一個低階函式,僅適用於非常特殊的情況。無法保證 func 會盡快被呼叫。如果主執行緒忙於執行系統呼叫,則在系統呼叫返回之前不會呼叫 func。此函式通常不適合從任意 C 執行緒呼叫 Python 程式碼。相反,請使用PyGILState API。
在 3.1 版本加入。
3.9 版更改: 如果在子直譯器中呼叫此函式,則函式 func 現在被安排從子直譯器呼叫,而不是從主直譯器呼叫。每個子直譯器現在都有自己的預定呼叫列表。
3.12 版更改: 此函式現在始終將 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¶
當報告行號事件時,作為 what 引數傳遞給
Py_tracefunc
函式(但不是剖析函式)的值。可以透過在該幀上將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()
函式。呼叫者必須具有 已附加的執行緒狀態。
-
void PyEval_SetProfileAllThreads(Py_tracefunc func, PyObject *obj)¶
與
PyEval_SetProfile()
類似,但將剖析函式設定到屬於當前直譯器的所有正在執行的執行緒中,而不是僅設定到當前執行緒中。呼叫者必須具有 已附加的執行緒狀態。
與
PyEval_SetProfile()
一樣,此函式在設定所有執行緒的剖析函式時會忽略任何引發的異常。
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()
函式。呼叫者必須具有 已附加的執行緒狀態。
-
void PyEval_SetTraceAllThreads(Py_tracefunc func, PyObject *obj)¶
與
PyEval_SetTrace()
類似,但將跟蹤函式設定到屬於當前直譯器的所有正在執行的執行緒中,而不是僅設定到當前執行緒中。呼叫者必須具有 已附加的執行緒狀態。
與
PyEval_SetTrace()
一樣,此函式在設定所有執行緒的跟蹤函式時會忽略任何引發的異常。
3.12 新版功能.
引用跟蹤¶
在 3.13 版本加入。
-
typedef int (*PyRefTracer)(PyObject*, int event, void *data)¶
使用
PyRefTracer_SetTracer()
註冊的跟蹤函式的型別。第一個引數是一個剛剛建立的 Python 物件(當 event 設定為PyRefTracer_CREATE
時)或即將被銷燬的 Python 物件(當 event 設定為PyRefTracer_DESTROY
時)。data 引數是呼叫PyRefTracer_SetTracer()
時提供的不透明指標。
在 3.13 版本加入。
-
int PyRefTracer_CREATE¶
當 Python 物件被建立時,傳遞給
PyRefTracer
函式的 event 引數的值。
-
int PyRefTracer_DESTROY¶
當 Python 物件被銷燬時,傳遞給
PyRefTracer
函式的 event 引數的值。
-
int PyRefTracer_SetTracer(PyRefTracer tracer, void *data)¶
註冊一個引用跟蹤函式。當建立新的 Python 物件或物件即將被銷燬時,將呼叫此函式。如果提供了 data,它必須是一個不透明指標,在呼叫跟蹤函式時將提供該指標。成功時返回
0
。出錯時設定異常並返回-1
。請注意,跟蹤函式不得在內部建立 Python 物件,否則呼叫將是重入的。跟蹤器也不得清除任何現有異常或設定異常。每次呼叫跟蹤函式時,執行緒狀態都將處於活動狀態。
呼叫此函式時必須存在附加執行緒狀態。
在 3.13 版本加入。
-
PyRefTracer PyRefTracer_GetTracer(void **data)¶
獲取已註冊的引用跟蹤函式以及在呼叫
PyRefTracer_SetTracer()
時註冊的不透明資料指標的值。如果未註冊跟蹤器,此函式將返回 NULL 並將 data 指標設定為 NULL。呼叫此函式時必須存在附加執行緒狀態。
在 3.13 版本加入。
高階偵錯程式支援¶
這些函式僅供高階除錯工具使用。
-
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 提供的 API:使用執行緒鍵和函式將 void* 值與每個執行緒關聯。
請注意,Python.h
不包含 TLS API 的宣告,您需要包含 pythread.h
才能使用執行緒本地儲存。
備註
這些 API 函式均不代表 void* 值處理記憶體管理。您需要自行分配和釋放它們。如果 void* 值恰好是 PyObject*,則這些函式也不會對它們進行引用計數操作。
執行緒特定儲存 (TSS) API¶
引入 TSS API 是為了取代 CPython 直譯器中現有 TLS API 的使用。此 API 使用新型別 Py_tss_t
而不是 int 來表示執行緒鍵。
在 3.7 版本加入。
參見
“CPython 中執行緒本地儲存的新 C-API”(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()¶
- 自 3.7 版本起成為 穩定ABI 的一部分。
返回與用
Py_tss_NEEDS_INIT
初始化的值狀態相同的值,或者在動態分配失敗的情況下返回NULL
。
-
void PyThread_tss_free(Py_tss_t *key)¶
- 自 3.7 版本起成為 穩定ABI 的一部分。
釋放由
PyThread_tss_alloc()
分配的給定 key,首先呼叫PyThread_tss_delete()
以確保任何關聯的執行緒區域性變數已被取消分配。如果 key 引數是NULL
,則此操作為空操作。備註
一個已釋放的鍵會變成一個懸空指標。你應該將該鍵重置為
NULL
。
方法¶
這些函式的引數 key 不得為 NULL
。此外,如果給定的 Py_tss_t
未經 PyThread_tss_create()
初始化,則 PyThread_tss_set()
和 PyThread_tss_get()
的行為是未定義的。
-
int PyThread_tss_is_created(Py_tss_t *key)¶
- 自 3.7 版本起成為 穩定ABI 的一部分。
如果給定的
Py_tss_t
已由PyThread_tss_create()
初始化,則返回非零值。
-
int PyThread_tss_create(Py_tss_t *key)¶
- 自 3.7 版本起成為 穩定ABI 的一部分。
成功初始化 TSS 鍵後返回零值。如果 key 引數指向的值未由
Py_tss_NEEDS_INIT
初始化,則行為未定義。此函式可以在同一個鍵上重複呼叫——在已初始化的鍵上呼叫它是一個空操作並立即返回成功。
-
void PyThread_tss_delete(Py_tss_t *key)¶
- 自 3.7 版本起成為 穩定ABI 的一部分。
銷燬 TSS 鍵,以忘記所有執行緒中與該鍵關聯的值,並將該鍵的初始化狀態更改為未初始化。銷燬的鍵可以再次由
PyThread_tss_create()
初始化。此函式可以在同一個鍵上重複呼叫——在已銷燬的鍵上呼叫它是一個空操作。
執行緒區域性儲存 (TLS) API¶
自 3.7 版本棄用: 此 API 已被 執行緒特定儲存 (TSS) API 取代。
備註
此版本的 API 不支援那些本地 TLS 鍵定義方式無法安全地轉換為 int
的平臺。在此類平臺上,PyThread_create_key()
將立即返回失敗狀態,並且其他 TLS 函式在此類平臺上都將是空操作。
由於上述相容性問題,新程式碼不應使用此 API 版本。
同步原語¶
C-API 提供基本的互斥鎖。
-
type PyMutex¶
互斥鎖。
PyMutex
應初始化為零,表示未鎖定狀態。例如:PyMutex mutex = {0};
PyMutex
的例項不應被複制或移動。PyMutex
的內容和地址都有意義,它必須保持在記憶體中固定、可寫的位置。備註
PyMutex
目前佔用一個位元組,但其大小應被視為不穩定。未來 Python 版本中可能會更改大小,而無需棄用期。在 3.13 版本加入。
-
void PyMutex_Lock(PyMutex *m)¶
鎖定互斥體 m。如果另一個執行緒已將其鎖定,則呼叫執行緒將阻塞,直到互斥體被解鎖。在阻塞期間,如果存在 執行緒狀態,執行緒將暫時分離該執行緒狀態。
在 3.13 版本加入。
-
int PyMutex_IsLocked(PyMutex *m)¶
如果互斥體 m 當前被鎖定,則返回非零值,否則返回零。
備註
此函式僅用於斷言和除錯,不應用於做出併發控制決策,因為鎖狀態可能在檢查後立即更改。
在 3.14 版本加入。
Python 臨界區 API¶
臨界區 API 在 自由執行緒 CPython 的物件級鎖之上提供了一個死鎖避免層。它們旨在取代對 全域性直譯器鎖 的依賴,並且在具有全域性直譯器鎖的 Python 版本中是空操作。
臨界區旨在用於 C-API 擴充套件中實現的自定義型別。它們通常不應與 list
和 dict
等內建型別一起使用,因為它們的公共 C-API 內部已使用臨界區,但 PyDict_Next()
是一個值得注意的例外,它需要外部獲取臨界區。
臨界區透過隱式暫停活動臨界區來避免死鎖,因此,它們不提供傳統鎖(如 PyMutex
)提供的排他訪問。當臨界區啟動時,將獲取物件的物件級鎖。如果在臨界區內執行的程式碼呼叫 C-API 函式,則它可以暫停臨界區,從而釋放物件級鎖,以便其他執行緒可以為同一物件獲取物件級鎖。
接受 PyMutex
指標而不是 Python 物件的變體也可用。在沒有 PyObject
的情況下,使用這些變體來啟動臨界區——例如,當使用不擴充套件或包裝 PyObject
但仍需要以可能導致死鎖的方式呼叫 C API 的 C 型別時。
宏使用的函式和結構體在 C 宏不可用的情況下暴露出來。它們只能按照給定的宏展開方式使用。請注意,結構體的大小和內容可能在未來的 Python 版本中發生變化。
備註
需要同時鎖定兩個物件的操作必須使用 Py_BEGIN_CRITICAL_SECTION2
。你 不能 使用巢狀臨界區一次鎖定多個物件,因為內部臨界區可能會暫停外部臨界區。此 API 不提供一次鎖定兩個以上物件的方法。
用法示例:
static PyObject *
set_field(MyObject *self, PyObject *value)
{
Py_BEGIN_CRITICAL_SECTION(self);
Py_SETREF(self->field, Py_XNewRef(value));
Py_END_CRITICAL_SECTION();
Py_RETURN_NONE;
}
在上面的例子中,Py_SETREF
呼叫 Py_DECREF
,後者可以透過物件的解除分配函式呼叫任意程式碼。臨界區 API 透過允許執行時在終結器觸發的程式碼阻塞並呼叫 PyEval_SaveThread()
時臨時暫停臨界區,從而避免了由於重入和鎖順序導致的潛在死鎖。
-
Py_BEGIN_CRITICAL_SECTION(op)¶
獲取物件 op 的物件級鎖並開始一個臨界區。
在自由執行緒構建中,此宏展開為
{ PyCriticalSection _py_cs; PyCriticalSection_Begin(&_py_cs, (PyObject*)(op))
在預設構建中,此宏展開為
{
。在 3.13 版本加入。
-
Py_BEGIN_CRITICAL_SECTION_MUTEX(m)¶
鎖定互斥鎖 m 並開始一個臨界區。
在自由執行緒構建中,此宏展開為
{ PyCriticalSection _py_cs; PyCriticalSection_BeginMutex(&_py_cs, m)
請注意,與
Py_BEGIN_CRITICAL_SECTION
不同,宏的引數沒有強制轉換——它必須是一個PyMutex
指標。在預設構建中,此宏展開為
{
。在 3.14 版本加入。
-
Py_END_CRITICAL_SECTION()¶
結束臨界區並釋放物件級鎖。
在自由執行緒構建中,此宏展開為
PyCriticalSection_End(&_py_cs); }
在預設構建中,此宏展開為
}
。在 3.13 版本加入。
-
Py_BEGIN_CRITICAL_SECTION2(a, b)¶
獲取物件 a 和 b 的物件級鎖並開始一個臨界區。鎖以一致的順序(最低地址優先)獲取,以避免鎖順序死鎖。
在自由執行緒構建中,此宏展開為
{ PyCriticalSection2 _py_cs2; PyCriticalSection2_Begin(&_py_cs2, (PyObject*)(a), (PyObject*)(b))
在預設構建中,此宏展開為
{
。在 3.13 版本加入。
-
Py_BEGIN_CRITICAL_SECTION2_MUTEX(m1, m2)¶
鎖定互斥鎖 m1 和 m2 並開始一個臨界區。
在自由執行緒構建中,此宏展開為
{ PyCriticalSection2 _py_cs2; PyCriticalSection2_BeginMutex(&_py_cs2, m1, m2)
請注意,與
Py_BEGIN_CRITICAL_SECTION2
不同,宏的引數沒有強制轉換——它們必須是PyMutex
指標。在預設構建中,此宏展開為
{
。在 3.14 版本加入。
-
Py_END_CRITICAL_SECTION2()¶
結束臨界區並釋放物件級鎖。
在自由執行緒構建中,此宏展開為
PyCriticalSection2_End(&_py_cs2); }
在預設構建中,此宏展開為
}
。在 3.13 版本加入。