嵌入式Python Segfaults

时间:2012-01-17 00:16:38

标签: c++ python multithreading segmentation-fault embedding

我的多线程应用会在调用PyImport_ImportModule("my_module")时发生段错误。

BT将发布在底部。

一些背景知识:

  1. 我的应用程序创建了许多派生C ++类的多个实例,并运行基类的Run()函数,该函数使用虚拟方法来确定要执行的操作。
  2. 一个派生类在Grasp_Behavior(模块)中使用Python类grasp_behavior(类)
  3. 经过广泛阅读后,我使用Python API来实现(2)(exerpts below)
  4. 我生成了所有类的2个实例,并在“并行”中运行它们(python interpr并不真正并行运行)
  5. 我尝试在PyImport_ImportModule
  6. 生成所述类的另一个实例,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
    

2 个答案:

答案 0 :(得分:4)

首先,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_EnsurePyGILState_Release,但是您应该考虑C ++异常。如果PyGILState_Release抛出,则不会调用Run()

PyGILState调用的一个不错的属性是,无论GIL是否被获取,您都可以使用它们,并且它们将做正确的事情,与旧的API不同。

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

答案 1 :(得分:1)

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