嵌入式 Python 段错误
我的多线程应用在调用 PyImport_ImportModule("my_module")
时出现段错误。
BT 将发布在底部。
一些背景:
- 我的应用程序创建了许多派生 C++ 类的多个实例,并运行基类的 Run() 函数,该函数使用虚拟方法来确定要执行的操作。
- 一个派生类使用 Python 类,
grasp_behavior
(module) 中的Grasp_Behavior
(class) - 使用 Python API 来实现 (2)(下面摘录)
- 经过大量阅读后,我 生成所述类的 2 个实例,并以“并行”方式运行它们(python interpr 并不真正并行运行)
- 我尝试生成所述类的另一个实例,segfault 在
PyImport_ImportModule
我的想法是,也许我无法在同一个解释器中导入模块两次。但我不知道如何检查它。我假设我需要查看 grasp_behavior
是否在字典中,但我不知道是哪一个,也许我得到了 __main__
模块的字典?
但我可能是错的,任何建议都会非常有帮助!
在构造函数中:
//Check if Python is Initialized, otherwise initialize it
if(!Py_IsInitialized())
{
std::cout << "[GraspBehavior] InitPython: Initializing the Python Interpreter" << std::endl;
Py_Initialize();
PyEval_InitThreads(); //Initialize Python thread ability
PyEval_ReleaseLock(); //Release the implicit lock on the Python GIL
}
// --- Handle Imports ----
PyObject * pModule = PyImport_ImportModule("grasp_behavior");
if(pModule == NULL)
{
std::cout << "[GraspBehavior] InitPython: Unable to import grasp_behavior module: ";
PyErr_Print();
}
// --- Get our Class Pointer From the Module ...
PyObject * pClass = PyObject_GetAttrString(pModule, "Grasp_Behavior");
if(pClass == NULL)
{
std::cout << "[GraspBehavior] InitPython: Unable to get Class from Module: ";
PyErr_Print();
}
Py_DECREF(pModule); //clean up, this is a new reference
behavior_instance_ = PyObject_Call(pClass, pArguments_Tuple, pArguments_Dict);
if(behavior_instance_ == NULL)
{
std::cout << "[GraspBehavior] InitPython: Couldn't generate instance: ";
PyErr_Print();
}
Py_DECREF(pArguments_Tuple);
Py_DECREF(pArguments_Dict);
Py_DECREF(pClass);
这里,注意,如果Python解释器还没有初始化的话,我只初始化它。我假设它在整个过程中被初始化。
在 Run()
方法中(从 boost 线程运行):
std::cout << "[GraspBehavior] PerformBehavior: Acquiring Python GIL Lock ..." << std::endl;
PyGILState_STATE py_gilstate;
py_gilstate = PyGILState_Ensure();
/* ---- Perform Behavior Below ----- */
std::vector<std::pair<double, double> > desired_body_offsets;
//desired_body_offsets.push_back( std::pair<double, double>(0.6, 0));
PyObject * base_positions = GetTrialBasePositions(my_env_, desired_body_offsets);
PyObject * grasps = EvaluateBasePositions(my_env_, base_positions);
//Did we get any grasps? What do we do with them? [TODO]
if(grasps != NULL)
{
std::cout << grasps->ob_type->tp_name << std::endl;
std::cout << "Number of grasps: " << PyList_Size(grasps) << std::endl;
successful_ = true;
}
/* --------------------------------- */
std::cout << "[GraspBehavior] PerformBehavior: Releasing Python GIL Lock ..." << std::endl;
PyGILState_Release(py_gilstate);
在这里,我使用了 PyGILState
锁。我读了一段时间,似乎很多人链接的一些文章都使用了 Python 中较旧的锁定样式……也许我可能必须切换它。
回溯:
Program received signal SIGSEGV, Segmentation fault.
0x00007fffee9c4330 in ?? () from /usr/lib/libpython2.6.so.1.0
(gdb) bt
#0 0x00007fffee9c4330 in ?? () from /usr/lib/libpython2.6.so.1.0
#1 0x00007fffee99ff09 in PyEval_GetGlobals ()
from /usr/lib/libpython2.6.so.1.0
#2 0x00007fffee9bd993 in PyImport_Import () from /usr/lib/libpython2.6.so.1.0
#3 0x00007fffee9bdbec in PyImport_ImportModule ()
from /usr/lib/libpython2.6.so.1.0
#4 0x000000000042d6f0 in GraspBehavior::InitPython (this=0x7948690)
at grasp_behavior.cpp:241
My multi-threaded app segfaults on a call to PyImport_ImportModule("my_module")
.
The BT will be posted at the bottom.
Some background:
- My app creates multiple instances of many derived C++ classes and runs the base class's
Run()
function which uses a virtual method to determine what to do. - One derived class uses a Python Class,
Grasp_Behavior
(class) ingrasp_behavior
(module) - After extensive reading I have used the Python API to achieve (2) (exerpts below)
- I generate 2 instances of said class, and run them in "parallel" (python interpr doesn't really run parallel)
- I attempt to generate another instance of said class, segfault at
PyImport_ImportModule
My thoughts are that perhaps I cannot import a module twice in the same interpreter. But I can't figure out how to check it. I assume I need to see if grasp_behavior
is in a dictionary but I don't know which one, perhaps I get __main__
module's dictionary?
But I might be wrong, any advice would be incredibly helpful!
In the constructor:
//Check if Python is Initialized, otherwise initialize it
if(!Py_IsInitialized())
{
std::cout << "[GraspBehavior] InitPython: Initializing the Python Interpreter" << std::endl;
Py_Initialize();
PyEval_InitThreads(); //Initialize Python thread ability
PyEval_ReleaseLock(); //Release the implicit lock on the Python GIL
}
// --- Handle Imports ----
PyObject * pModule = PyImport_ImportModule("grasp_behavior");
if(pModule == NULL)
{
std::cout << "[GraspBehavior] InitPython: Unable to import grasp_behavior module: ";
PyErr_Print();
}
// --- Get our Class Pointer From the Module ...
PyObject * pClass = PyObject_GetAttrString(pModule, "Grasp_Behavior");
if(pClass == NULL)
{
std::cout << "[GraspBehavior] InitPython: Unable to get Class from Module: ";
PyErr_Print();
}
Py_DECREF(pModule); //clean up, this is a new reference
behavior_instance_ = PyObject_Call(pClass, pArguments_Tuple, pArguments_Dict);
if(behavior_instance_ == NULL)
{
std::cout << "[GraspBehavior] InitPython: Couldn't generate instance: ";
PyErr_Print();
}
Py_DECREF(pArguments_Tuple);
Py_DECREF(pArguments_Dict);
Py_DECREF(pClass);
Here, note that I only initialize the Python interpreter if it has not been initialized. I assume it gets initialized for the entire process.
In the Run()
method (ran from a boost thread):
std::cout << "[GraspBehavior] PerformBehavior: Acquiring Python GIL Lock ..." << std::endl;
PyGILState_STATE py_gilstate;
py_gilstate = PyGILState_Ensure();
/* ---- Perform Behavior Below ----- */
std::vector<std::pair<double, double> > desired_body_offsets;
//desired_body_offsets.push_back( std::pair<double, double>(0.6, 0));
PyObject * base_positions = GetTrialBasePositions(my_env_, desired_body_offsets);
PyObject * grasps = EvaluateBasePositions(my_env_, base_positions);
//Did we get any grasps? What do we do with them? [TODO]
if(grasps != NULL)
{
std::cout << grasps->ob_type->tp_name << std::endl;
std::cout << "Number of grasps: " << PyList_Size(grasps) << std::endl;
successful_ = true;
}
/* --------------------------------- */
std::cout << "[GraspBehavior] PerformBehavior: Releasing Python GIL Lock ..." << std::endl;
PyGILState_Release(py_gilstate);
Here, I have gone with PyGILState
lock. I read for a while and it seemed the some of the articles that many people are link use an older style of locking in Python ... perhaps I may have to switch this.
Backtrace:
Program received signal SIGSEGV, Segmentation fault.
0x00007fffee9c4330 in ?? () from /usr/lib/libpython2.6.so.1.0
(gdb) bt
#0 0x00007fffee9c4330 in ?? () from /usr/lib/libpython2.6.so.1.0
#1 0x00007fffee99ff09 in PyEval_GetGlobals ()
from /usr/lib/libpython2.6.so.1.0
#2 0x00007fffee9bd993 in PyImport_Import () from /usr/lib/libpython2.6.so.1.0
#3 0x00007fffee9bdbec in PyImport_ImportModule ()
from /usr/lib/libpython2.6.so.1.0
#4 0x000000000042d6f0 in GraspBehavior::InitPython (this=0x7948690)
at grasp_behavior.cpp:241
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
首先,GIL 释放时不得调用任何 Python API 函数(GIL 获取调用除外)。
此代码将崩溃:
完成设置后释放 GIL,然后根据需要重新获取(在
Run()
中)。此外,
PyEval_ReleaseLock
已弃用,在这种情况下您应该使用PyEval_SaveThread
。这将保存线程状态并释放 GIL。
然后,在开始完成解释器之前,执行以下操作:
传递
PyEval_SaveThread
调用的返回值。在
Run()
中,您应该像现在一样使用PyGILState_Ensure
和PyGILState_Release
,但您应该考虑 C++ 异常。现在,如果Run()
抛出异常,则不会调用PyGILState_Release
。PyGILState
调用的一个很好的特性是,无论是否获取 GIL,您都可以使用它们,并且它们将执行正确的操作,这与旧版 API 不同。另外,您应该在主线程启动时(在其他线程启动之前)初始化解释器一次,并在关闭除主线程之外的所有线程后完成。
First of all, you must not call any Python API functions when the GIL is released (except the GIL acquiring calls).
This code will crash:
Release the GIL after you're done setting up and then re-acquire as needed (in your
Run()
).Also,
PyEval_ReleaseLock
is deprecated, you should usePyEval_SaveThread
in this case instead.This will save the thread state and release the GIL.
Then, right before you start finalizing the interpreter, do this:
passing the return value of the
PyEval_SaveThread
call.In
Run()
, you should usePyGILState_Ensure
andPyGILState_Release
, as you do now, but you should think about C++ exceptions. Right nowPyGILState_Release
will not be called ifRun()
throws.One nice property of the
PyGILState
calls is that you can use them no matter if the GIL is acquired or not and they will do the right thing, unlike older APIs.Also, you should initialize the interpreter once at startup in the main thread (before other threads are started) and finalize after shutting down all threads but the main one.
Boost 是否提供了与 pthread_once() 函数等效的道德功能,该函数允许某些初始化任务仅运行一次,无论有多少线程尝试同时运行它?如果这是我要调试的问题,我会尝试保护
PyImport_ImportModule
防止多个线程中的多个调用,并使用标准工具来实现这将是我的第一次尝试。Does Boost provide a moral equivalent to the
pthread_once()
function that allows some initialization task to be run exactly once, no matter how many threads try to run it simultaneously? If this were my problem to debug I'd try to guardPyImport_ImportModule
against multiple calls in multiple threads and using a standard tool for that would be my first attempt.