Python3 C++ 模块 - 异常 - GIL 未持有

Posted

技术标签:

【中文标题】Python3 C++ 模块 - 异常 - GIL 未持有【英文标题】:Python3 C++ Module - Exception - GIL not held 【发布时间】:2021-07-04 13:13:27 【问题描述】:

我已经创建了一个我遇到的问题的最小复制,试图创建一个基于 C++ 的 Python3 模块。构建环境为 CMake、Visual Studio Pro 2019、WinSDK 10.0.18362、Python 3.9.4。执行时:

python3 -c "import pymod"

我将在具有 NULL 访问权限的发布模式下遇到异常。在调试 Python 时,我得到了更多信息。

我的 CMakeLists.txt

cmake_minimum_required(VERSION 3.15)

project(pymod_minimal_repo_example)

find_package(Python3 COMPONENTS Interpreter Development)

add_library(pymod SHARED)
target_sources(pymod PRIVATE pymodmodule.cpp)
set_target_properties(pymod PROPERTIES SUFFIX ".pyd")
target_compile_options(pymod PRIVATE /Zi)
target_link_options(pymod PRIVATE /DEBUG:FULL)
target_link_libraries(pymod PRIVATE $Python3_LIBRARIES)
target_include_directories(pymod PRIVATE $Python3_INCLUDE_DIRS)

在此 python 参考之后:https://docs.python.org/3.9/extending/extending.html#a-simple-example 我创建了以下内容:

#define PY_SSIZE_T_CLEAN
#include <Python.h>

static struct PyModuleDef pymodmodule = 
    PyModuleDef_HEAD_INIT, // m_base
    "pymod",               // m_name
    NULL,                  // m_doc
    -1,                    // m_size - submod not support must be static struct
    NULL,                  // m_methods - no functions present
    NULL,                  // m_slots - must be NULL
    NULL,                  // m_traverse - not needed
    NULL,                  // m_clear - not needed
    NULL                   // m_free - not needed
;

PyMODINIT_FUNC PyInit_pymod(void) 
  return PyModule_Create(&pymodmodule);

它是一个功能无效的模块,它什么都不做,但仍应运行。请注意,定义了 m_methods 会产生同样的失败。

发生故障时,向控制台输出以下内容:

E:\projects\pymod\build\Debug>python -c "import pymod"
Fatal Python error: _PyInterpreterState_GET: the function must be called with the GIL held, but the GIL is released (the current Python thread state is NULL)
Python runtime state: unknown

WinDbg jit 调试器随后会发现问题。部分调用堆栈显示我的 PyInit_pymod 被调用,并且在创建 python 模块时,它级联失败:

0:000> k
 # Child-SP          RetAddr           Call Site
00 000000dc`abfed4a8 00007fff`71d51385 KERNELBASE!wil::details::DebugBreak+0x2
01 000000dc`abfed4b0 00007fff`71d511a8 python39_d!fatal_error_exit+0x15 [D:\a\1\s\Python\pylifecycle.c @ 2201] 
02 000000dc`abfed4e0 00007fff`71d4ea98 python39_d!fatal_error+0x1b8 [D:\a\1\s\Python\pylifecycle.c @ 2286] 
03 000000dc`abfed540 00007fff`71cbe730 python39_d!_Py_FatalErrorFunc+0x38 [D:\a\1\s\Python\pylifecycle.c @ 2302] 
04 000000dc`abfed580 00007fff`71aa8044 python39_d!_Py_FatalError_TstateNULL+0x10 [D:\a\1\s\Python\ceval.c @ 251] 
05 000000dc`abfed5b0 00007fff`71aa6cf2 python39_d!_PyInterpreterState_GET+0x34 [D:\a\1\s\Include\internal\pycore_pystate.h @ 105] 
06 000000dc`abfed5f0 00007fff`d0be13e7 python39_d!PyModule_Create2+0x12 [D:\a\1\s\Objects\moduleobject.c @ 168] 

>>>> 07 000000dc`abfed620 00007fff`751165dc pymod!PyInit_pymod+0x27 [E:\projects\pymod\pymodmodule.cpp @ 19]

08 000000dc`abfed660 00007fff`751167ee python39!_PyImport_LoadDynamicModuleWithSpec+0x104 [C:\A\34\s\Python\importdl.c @ 165] 
09 000000dc`abfed6d0 00007fff`75116749 python39!_imp_create_dynamic_impl+0x86 [C:\A\34\s\Python\import.c @ 2299] 
0a 000000dc`abfed700 00007fff`750cb94b python39!_imp_create_dynamic+0x39 [C:\A\34\s\Python\clinic\import.c.h @ 330] 
0b 000000dc`abfed730 00007fff`750ad500 python39!cfunction_vectorcall_FASTCALL+0x9b [C:\A\34\s\Objects\methodobject.c @ 426] 
0c 000000dc`abfed7a0 00007fff`750ad2ef python39!PyVectorcall_Call+0x5c [C:\A\34\s\Objects\call.c @ 248] 
0d 000000dc`abfed800 00007fff`750ad418 python39!_PyObject_Call+0x4f [C:\A\34\s\Objects\call.c @ 287] 
0e (Inline Function) --------`-------- python39!PyObject_Call+0xc [C:\A\34\s\Objects\call.c @ 293] 
0f 000000dc`abfed830 00007fff`7508c65f python39!do_call_core+0xb8 [C:\A\34\s\Python\ceval.c @ 5092] 
10 000000dc`abfed880 00007fff`75083963 python39!_PyEval_EvalFrameDefault+0x5d6f [C:\A\34\s\Python\ceval.c @ 3581] 
11 (Inline Function) --------`-------- python39!_PyEval_EvalFrame+0x13 [C:\A\34\s\Include\internal\pycore_ceval.h @ 40] 
12 000000dc`abfedbb0 00007fff`750855a7 python39!_PyEval_EvalCode+0x2b3 [C:\A\34\s\Python\ceval.c @ 4327] 
13 000000dc`abfedc80 00007fff`7508823d python39!_PyFunction_Vectorcall+0x257 [C:\A\34\s\Objects\call.c @ 396] 
14 000000dc`abfedd80 00007fff`7508812f python39!_PyEval_EvalFrameDefault+0x194d [C:\A\34\s\Python\ceval.c @ 3487] 
15 000000dc`abfee0b0 00007fff`750888e5 python39!_PyEval_EvalFrameDefault+0x183f [C:\A\34\s\Python\ceval.c @ 3504] 
16 000000dc`abfee3e0 00007fff`750888e5 python39!_PyEval_EvalFrameDefault+0x1ff5 [C:\A\34\s\Python\ceval.c @ 3518] 
17 000000dc`abfee710 00007fff`750888e5 python39!_PyEval_EvalFrameDefault+0x1ff5 [C:\A\34\s\Python\ceval.c @ 3518] 
18 000000dc`abfeea40 00007fff`750854c4 python39!_PyEval_EvalFrameDefault+0x1ff5 [C:\A\34\s\Python\ceval.c @ 3518] 

我找不到关于控制台消息的任何信息,也找不到 int3。我已尝试完全清除并重新安装/更新 Python。

谁能提供一些帮助或知道原因的方向?

编辑:修改 PyModule_pymod 函数以将 Pre 和 Post 消息显示到控制台。调试构建异常,Release 似乎不是:

PyMODINIT_FUNC PyInit_pymod(void) 
  printf("Pre-PyModule_Create\n");
  PyObject *obj = PyModule_Create(&pymodmodule);
  printf("Post-PyModule_Create\n");
  return obj;

发布:

E:\projects\pymod\build\Release>python -c "import pymod"
Pre-PyModule_Create
Post-PyModule_Create

调试:

E:\projects\pymod\build\Debug>python -c "import pymod"
Pre-PyModule_Create
Fatal Python error: _PyInterpreterState_GET: the function must be called with the GIL held, but the GIL is released (the current Python thread state is NULL)
Python runtime state: unknown

【问题讨论】:

GIL 是“全局解释器锁”,显然,在调用任何 Python 基础设施之前,您必须持有它。 如果我调用 Py_Initialize(),GIL 锁定问题将停止,但这显然是针对嵌入在 C 代码中的 python。模块示例和教程不使用它。添加 Py_Initialize 时,我得到一个错误:SystemError:pymod 的初始化没有返回扩展模块。 PyModule_Create 执行时,我可以检查返回的 PyObject* 并查看 py_type 成员设置为“模块”。因此确信 Py_Initialize 不是解决方案。 遇到了同样的问题,但完全相同的代码(以及相同的 CmakeLists.txt)在 UNIX 上运行良好...没有 GIL 相关问题... 【参考方案1】:

我遇到了和你一样的问题,在调试 TLS 结构时,我注意到 Visual Studio 加载了 python310.dll 而不是 python310_d.dll。

长话短说,当你定义_DEBUG宏时,Python.h会使用python310_d.lib,所以你需要运行调试版的Python:

python_d -c "import pymod"

我在使用 pybind11 时没有遇到这个问题,这让我更加困惑。事实证明,pybind11 在包含 Python.h 时取消了 _DEBUG 宏的定义,因此它始终使用 python310.lib 而不是 python310_d.lib,即使用于调试构建也是如此。

由于我没有在 Visual Studio 中看到使用 python_d 解释器的选项,我最终使用发布库使用以下代码调试构建:

#if defined(_MSC_VER) && defined(_DEBUG)
#undef _DEBUG
#include <Python.h>
#define _DEBUG 1
#else
#include <Python.h>
#endif

作为旁注,当您链接到 python310_d.dll 时,您必须将调试模块命名为 pymod_d.pyd 而不是 pymod.pyd。

【讨论】:

以上是关于Python3 C++ 模块 - 异常 - GIL 未持有的主要内容,如果未能解决你的问题,请参考以下文章

Python3 源码阅读 深入了解Python GIL

python3多线程和GIL全局解释器所

Python3 并发编程3

Python GIL:并发 C++ 嵌入

Python3多线程爬虫实例讲解

在 C++ 代码中发布 Python GIL