如果我在不同的pybind11 :: scoped_interpreter会话中两次导入一个外部模块,则应用程序在eval函数的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
重新启动解释器。使用pybind11创建的模块可以安全地重新初始化。但是,Python本身无法完全卸载二进制扩展模块,并且在重新启动解释器方面存在一些警告。所有详细信息都可以在CPython文档中找到。简而言之,由于参考周期或用户创建的全局数据,并非所有解释器的内存都可能被释放。
因此无法两次调用Python解释器吗?我有一个包含辅助numpy函数的python文件,我需要在C ++算法执行的不同点调用该函数。这是否意味着我不能做到这一点?
答案 0 :(得分:1)
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();
}
答案 1 :(得分:0)
根据这篇文章 https://www.boost.org/doc/libs/1_47_0/libs/python/todo.html#pyfinalize-safety,目前最好永远调用 PyFinalize,无论是直接调用还是通过 scoped_interpreter。
在调用 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 已承诺完成这项工作。