Embedded Python Segfaults

Embedded Python Segfaults

本文关键字:Segfaults Python Embedded      更新时间:2023-10-16

我的多线程应用程序在调用PyImport_ImportModule("my_module")时出错。

BT将张贴在底部。

一些背景:

  1. 我的应用程序创建了许多派生C++类的多个实例,并运行基类的Run()函数,该函数使用虚拟方法来确定要做什么
  2. 一个派生类使用Python类grasp_behavior(模块)中的Grasp_Behavior(类)
  3. 经过大量阅读,我使用Python API实现了(2)(下面的练习)
  4. 我生成了上述类的2个实例,并以"并行"方式运行它们(python-interp并不是真正并行运行)
  5. 我尝试在PyImport_ImportModule生成所述类的另一个实例segfault

我的想法是,也许我不能在同一个解释器中两次导入一个模块。但是我不知道如何检查。我想我需要看看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()方法中(从提升线程运行):

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

传递CCD_ 13调用的返回值。

Run()中,您应该像现在一样使用PyGILState_EnsurePyGILState_Release,但您应该考虑C++异常。现在,如果Run()抛出,则不会调用PyGILState_Release

PyGILState调用的一个很好的特性是,无论是否获取了GIL,都可以使用它们,而且它们会做正确的事情,这与旧的API不同。

此外,您应该在主线程中启动时初始化解释器一次(在其他线程启动之前),并在关闭除主线程之外的所有线程后完成。

Boost是否提供了一个与pthread_once()函数在道义上等效的函数,该函数允许一些初始化任务只运行一次,无论有多少线程试图同时运行它?如果这是我调试的问题,我会尝试保护PyImport_ImportModule不受多个线程中的多个调用的影响,并且使用标准工具将是我的第一次尝试。