我的多线程应用程序会在调用PyImport_ImportModule(“my_module”)时发生段错误.
BT将在底部发布.
一些背景:
>我的应用程序创建了许多派生C类的多个实例,并运行基类的Run()函数,该函数使用虚方法来确定要执行的操作.
>一个派生类在grip_behavior(模块)中使用Python类,Grasp_Behavior(类)
>经过广泛阅读后,我使用了Python API来实现(2)(exerpts below)
>我生成了所述类的2个实例,并在“并行”中运行它们(python interpr并不真正并行运行)
>我尝试在PyImport_ImportModule中生成所述类的另一个实例segfault
我的想法是,也许我不能在同一个解释器中导入两次模块.但我无法弄清楚如何检查它.我想我需要看看grip_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
解决方法:
首先,在GIL发布时不得调用任何Python API函数(GIL获取调用除外).
此代码将崩溃:
PyEval_ReleaseLock();
PyObject * pModule = PyImport_ImportModule("grasp_behavior");
完成设置后释放GIL,然后根据需要重新获取(在Run()中).
另外,不推荐使用PyEval_ReleaseLock,在这种情况下应该使用PyEval_SaveThread.
PyThreadState* tstate = PyEval_SaveThread();
这将保存线程状态并释放GIL.
然后,在开始完成解释器之前,执行以下操作:
PyEval_RestoreThread(tstate);
传递PyEval_SaveThread调用的返回值.
在Run()中,您应该像现在一样使用PyGILState_Ensure和PyGILState_Release,但是您应该考虑C异常.现在,如果抛出Run(),则不会调用PyGILState_Release.
PyGILState调用的一个不错的属性是,无论GIL是否被获取,你都可以使用它们,并且它们将做正确的事情,不像旧的API.
此外,您应该在主线程启动时(在其他线程启动之前)初始化解释器一次,并在关闭除主要线程之外的所有线程之后完成.