当嵌入式 Python 解释器尝试第二次导入外部模块时,C++ 应用程序崩溃

pse*_*vin 3 c++ python pybind11

如果我在不同的 pybind11::scoped_interpreter 会话中两次导入外部模块,应用程序将在 eval.h 中的函数 eval 中崩溃,如下行:

PyObject *result = PyRun_String(buffer.c_str(), start, global.ptr(), local.ptr());
Run Code Online (Sandbox Code Playgroud)

Exception thrown at 0x00007FFD710C4E0C (multiarray.cp36-win_amd64.pyd) in pybind-test.exe: 0xC0000005: Access violation writing location 0x000000000000000A.
Run Code Online (Sandbox Code Playgroud)

可重现的示例代码

namespace py = pybind11;
void test() {
    try {
        py::scoped_interpreter guard{};
        py::object mainScope = py::module::import("__main__").attr("__dict__");
        py::exec(
            "import numpy\n",
            mainScope);
    }
    catch (py::error_already_set const &pythonErr) {  std::cout << pythonErr.what(); }
}
int main() {
    test();   // Runs fine
    test();   // Crashes at py::exec
}
Run Code Online (Sandbox Code Playgroud)

我觉得这与 pybind11 的 embed.h 中的注释有关:

可以通过initialize_interpreter再次调用来重新启动解释器。使用 pybind11 创建的模块可以安全地重新初始化。但是,Python 本身不能完全卸载二进制扩展模块,并且在解释器重新启动方面有几个注意事项。所有细节都可以在 CPython 文档中找到。简而言之,并不是所有的解释器内存都可以被释放,无论是由于引用循环还是用户创建的全局数据。

那么有没有办法两次调用Python解释器呢?我有一个包含辅助 numpy 函数的 python 文件,我需要在 C++ 算法执行的不同点调用这些函数。这是否意味着我不能这样做?

pse*_*vin 6

pybind11 github repo的讨论中转述。

而不是使用py::scoped_interpreterusepy::initialize_interpreterpy::finalize_interpreter。根据需要多次呼叫口译员。

警告:“Python 解释器不是完全线程安全的为了支持多线程 Python 程序,有一个全局锁,称为全局解释器锁或GIL ”。

用法示例:

namespace py = pybind11;
void test() {
    try {
        py::object mainScope = py::module::import("__main__").attr("__dict__");
        py::exec(
            "import numpy\n",
            mainScope);
    }
    catch (py::error_already_set const &pythonErr) {  std::cout << pythonErr.what(); }
}
int main() {
   py::initialize_interpreter();
    test();  
    test();   
    py::finalize_interpreter();
}
Run Code Online (Sandbox Code Playgroud)