简体   繁体   English

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

[英]C++ application crashes when embedded Python interpreter tries to import external module a second time

If I import an external module twice in different pybind11::scoped_interpreter sessions, the application crashes in eval.h in function eval at following line:如果我在不同的 pybind11::scoped_interpreter 会话中两次导入外部模块,应用程序将在 eval.h 中的函数 eval 中崩溃,如下行:

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

with

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

Reproducible example code可重现的示例代码

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
}

I feel like this has to do with the comment in pybind11's embed.h:我觉得这与 pybind11 的 embed.h 中的注释有关:

The interpreter can be restarted by calling initialize_interpreter again.可以通过再次调用initialize_interpreter来重新启动解释initialize_interpreter Modules created using pybind11 can be safely re-initialized.使用 pybind11 创建的模块可以安全地重新初始化。 However, Python itself cannot completely unload binary extension modules and there are several caveats with regard to interpreter restarting.但是,Python 本身不能完全卸载二进制扩展模块,并且在解释器重新启动方面有几个注意事项。 All the details can be found in the CPython documentation.所有细节都可以在 CPython 文档中找到。 In short, not all interpreter memory may be freed, either due to reference cycles or user-created global data.简而言之,并不是所有的解释器内存都可以被释放,无论是由于引用循环还是用户创建的全局数据。

So there is no way to call the Python interpreter twice?那么有没有办法两次调用Python解释器呢? I have a python file containing helper numpy functions that I need to call at different points of algorithm execution from C++.我有一个包含辅助 numpy 函数的 python 文件,我需要在 C++ 的算法执行的不同点调用这些函数。 Does this mean I can't do that?这是否意味着我不能这样做?

Paraphrasing from the discussion at pybind11 github repo.pybind11 github repo的讨论中转述。

Instead of using py::scoped_interpreter use py::initialize_interpreter and py::finalize_interpreter .而不是使用py::scoped_interpreter使用py::initialize_interpreterpy::finalize_interpreter Call the interpreter in between as many times as you like.根据需要多次呼叫口译员。

Caveat:, “the Python interpreter is not fully thread-safe In order to support multi-threaded Python programs, there's a global lock, called the global interpreter lock or GIL “.警告:“Python 解释器不是完全线程安全的为了支持多线程 Python 程序,有一个全局锁,称为全局解释器锁或GIL ”。

Example usage:用法示例:

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();
}

It might currently be better to never call PyFinalize, either directly, or via scoped_interpreter, according to this article https://www.boost.org/doc/libs/1_47_0/libs/python/todo.html#pyfinalize-safety .根据这篇文章https://www.boost.org/doc/libs/1_47_0/libs/python/todo.html#pyfinalize-safety ,目前最好不要直接或通过 scoped_interpreter 调用 PyFinalize。

Errors can result when re-loading modules after calling finalize and then initialize again.在调用 finalize 后重新加载模块时可能会导致错误,然后再次初始化。 I ran into that when following the current approved answer https://stackoverflow.com/a/51069948/5994043 .我在遵循当前批准的答案https://stackoverflow.com/a/51069948/5994043时遇到了这个问题。

https://www.boost.org/doc/libs/1_47_0/libs/python/todo.html#pyfinalize-safety https://www.boost.org/doc/libs/1_47_0/libs/python/todo.html#pyfinalize-safety

PyFinalize Safety: Currently Boost.Python has several global (or function-static) objects whose existence keeps reference counts from dropping to zero until the Boost.Python shared object is unloaded. PyFinalize 安全:目前 Boost.Python 有几个全局(或函数静态)对象,它们的存在使引用计数不会下降到零,直到 Boost.Python 共享对象被卸载。 This can cause a crash because when the reference counts do go to zero, there's no interpreter.这可能会导致崩溃,因为当引用计数变为零时,没有解释器。 In order to make it safe to call PyFinalize() we must register an atexit routine which destroys these objects and releases all Python reference counts so that Python can clean them up while there's still an interpreter.为了确保调用 PyFinalize() 的安全性,我们必须注册一个 atexit 例程,该例程会销毁这些对象并释放所有 Python 引用计数,以便 Python 可以在仍有解释器的情况下清理它们。 Dirk Gerrits has promised to do this job. Dirk Gerrits 已承诺完成这项工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 当dll具有嵌入式Python解释器时,将函数从dll(用C ++编程)导入Python脚本 - Import functions from dll (programmed in C++) into Python script when the dll has an embedded Python interpreter 如何中断C ++应用程序中嵌入的python解释器 - How to interrupt python interpreter embedded in C++ application python - C++ 嵌入式解释器和对象 - python - c++ embedded interpreter and objects 在 C++ 应用程序中从嵌入式 Python 调用时,Numpy 导入在多数组扩展库上失败 - Numpy import fails on multiarray extension library when called from embedded Python within a C++ application Python 3.8:运行外部 C++ 代码:无法导入模块 - Python 3.8: Running External C++ Code: Unable to Import Module 嵌入式 Python:清理时使用 C++ 多线程的子解释器出现致命错误 - Embedded Python: fatal error in sub-interpreter with C++ multi-thread when cleanup 在(Windows)C ++应用程序中嵌入Python解释器 - Embed a Python interpreter in a (Windows) C++ application 如何使用C ++函数扩展嵌入式python解释器? - How can I extend an embedded python interpreter with C++ functions? 从 C++ 嵌入式解释器捕获 python 窗口输出 - Capturing python windowed output from C++ embedded interpreter 将C ++类实例暴露给python嵌入式解释器 - Exposing a C++ class instance to a python embedded interpreter
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM