簡體   English   中英

當嵌入式 Python 解釋器嘗試第二次導入外部模塊時,C++ 應用程序崩潰

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

如果我在不同的 pybind11::scoped_interpreter 會話中兩次導入外部模塊,應用程序將在 eval.h 中的函數 eval 中崩潰,如下行:

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來重新啟動解釋initialize_interpreter 使用 pybind11 創建的模塊可以安全地重新初始化。 但是,Python 本身不能完全卸載二進制擴展模塊,並且在解釋器重新啟動方面有幾個注意事項。 所有細節都可以在 CPython 文檔中找到。 簡而言之,並不是所有的解釋器內存都可以被釋放,無論是由於引用循環還是用戶創建的全局數據。

那么有沒有辦法兩次調用Python解釋器呢? 我有一個包含輔助 numpy 函數的 python 文件,我需要在 C++ 的算法執行的不同點調用這些函數。 這是否意味着我不能這樣做?

pybind11 github repo的討論中轉述。

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

根據這篇文章https://www.boost.org/doc/libs/1_47_0/libs/python/todo.html#pyfinalize-safety ,目前最好不要直接或通過 scoped_interpreter 調用 PyFinalize。

在調用 finalize 后重新加載模塊時可能會導致錯誤,然后再次初始化。 我在遵循當前批准的答案https://stackoverflow.com/a/51069948/5994043時遇到了這個問題。

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

PyFinalize 安全:目前 Boost.Python 有幾個全局(或函數靜態)對象,它們的存在使引用計數不會下降到零,直到 Boost.Python 共享對象被卸載。 這可能會導致崩潰,因為當引用計數變為零時,沒有解釋器。 為了確保調用 PyFinalize() 的安全性,我們必須注冊一個 atexit 例程,該例程會銷毀這些對象並釋放所有 Python 引用計數,以便 Python 可以在仍有解釋器的情況下清理它們。 Dirk Gerrits 已承諾完成這項工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM