代码之家  ›  专栏  ›  技术社区  ›  pseudomarvin

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

  •  1
  • pseudomarvin  · 技术社区  · 6 年前

    如果我在不同的pybind11::scoped_解释器会话中导入一个外部模块两次,那么在函数eval的eval.h中,应用程序将在以下行崩溃:

    PyObject *result = PyRun_String(buffer.c_str(), start, global.ptr(), local.ptr());
    

    具有

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

    可复制的示例代码

    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
    }
    

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

    解释器可以通过调用 initialize_interpreter 再一次。使用pybind11创建的模块可以安全地重新初始化。但是,python本身不能完全卸载二进制扩展模块,关于解释器的重新启动有几个注意事项。所有细节都可以在cpython文档中找到。简而言之,并非所有解释器内存都可以释放,这可能是由于引用周期或用户创建的全局数据。

    所以不能两次调用python解释器?我有一个包含帮助函数的Python文件,我需要在C++的算法执行的不同点调用它。这是不是意味着我做不到?

    1 回复  |  直到 6 年前
        1
  •  1
  •   pseudomarvin    6 年前

    pybind11 github repo.

    而不是使用 py::scoped_interpreter py::initialize_interpreter py::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();
    }