嵌入式 Python 段错误

Con*_*tin 4 c++ python multithreading embedding segmentation-fault

我的多线程应用程序在调用PyImport_ImportModule("my_module").

BT 将发布在底部。

一些背景:

  1. 我的应用程序创建许多派生 C++ 类的多个实例,并运行基类的Run()函数,该函数使用虚拟方法来确定要执行的操作。
  2. 一个派生类使用 Python 类, (模块Grasp_Behavior)中的(类)grasp_behavior
  3. 经过大量阅读我已经使用Python API来实现(2)(下面摘录)
  4. 我生成了该类的 2 个实例,并“并行”运行它们(python interpr 并不是真正并行运行)
  5. 我尝试生成该类的另一个实例,段错误位于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);
Run Code Online (Sandbox Code Playgroud)

这里,注意,如果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);
Run Code Online (Sandbox Code Playgroud)

在这里,我已经带锁了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
Run Code Online (Sandbox Code Playgroud)

yak*_*yak 5

首先,GIL 释放时不得调用任何 Python API 函数(GIL 获取调用除外)。

这段代码会崩溃:

PyEval_ReleaseLock();

PyObject * pModule = PyImport_ImportModule("grasp_behavior");
Run Code Online (Sandbox Code Playgroud)

完成设置后释放 GIL,然后根据需要重新获取(在您的 中Run())。

另外,PyEval_ReleaseLock已弃用,PyEval_SaveThread在这种情况下您应该使用它。

PyThreadState* tstate = PyEval_SaveThread();
Run Code Online (Sandbox Code Playgroud)

这将保存线程状态并释放 GIL。

然后,在开始最终确定解释器之前,执行以下操作:

PyEval_RestoreThread(tstate);
Run Code Online (Sandbox Code Playgroud)

传递调用的返回值PyEval_SaveThread

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

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

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

  • `PyEval_InitThreads` 创建(并获取)GIL。它应该从主线程调用。完成所有设置 `Py*` 调用后,您应该通过调用 `PyEval_SaveThread` 来释放 GIL(如果退出时不进行清理,则忽略返回值)。如果需要,您可以在此之前启动线程,“Run”方法中的“PyGILState_Ensure”调用将阻塞,直到主线程释放 GIL。当发生这种情况时,被阻止的线程之一将继续(并且该线程现在将拥有 GIL),直到它调用“PyGILState_Release”,此时另一个线程可能会进入。 (2认同)