从C++(或C)回调中调用python方法

Cha*_*nya 15 c c++ python callback python-c-api

我试图从C++调用python类中的方法.调用它的C++方法是C++回调.

在我尝试调用python方法时,在这个方法中,它正在给予segmentation fault.

我已经在一个全局变量中保存了一个python函数实例

// (pFunc is global variable of type PyObject*)
pFunc = PyDict_GetItemString(pDict, "PlxMsgWrapper");
Run Code Online (Sandbox Code Playgroud)

其中PlxMsgWrapper是一个python方法,将在回调中使用.

在回调中,参数创建为

PyObject* args = PyTuple_Pack(2, PyString_FromString(header.c_str()),
                                 PyString_FromString(payload.c_str()));
Run Code Online (Sandbox Code Playgroud)

在创建时

PyObject * pInstance = PyObject_CallObject(pFunc, args);
Run Code Online (Sandbox Code Playgroud)

在这一行中它给出了分段错误.在此之后,实际的python方法被称为

PyObject* recv_msg_func = PyObject_GetAttrString(module, (char *)"recvCallback");
args = PyTuple_Pack(1, pInstance);
PyObject_CallObject(recv_msg_func, args);
Run Code Online (Sandbox Code Playgroud)

msh*_*ldt 32

如果从C/C++回调调用Python函数,则需要执行一些操作.首先,当你保存你的python函数对象时,你需要增加引用计数:

Py_INCREF(pFunc)
Run Code Online (Sandbox Code Playgroud)

否则,Python不知道您持有对象引用,它可能会垃圾收集它,当您尝试从回调中使用它时会导致分段错误.

接下来你需要关心的是调用C/C++回调时正在运行的线程.如果你从另一个非Python创建的线程(即在套接字上接收数据的C/C++线程)回调,那么在调用任何Python API函数之前,你必须获得Python的全局解释器锁(GIL).否则你的程序的行为是不确定的.要获得GIL,您需要:

void callback() {
    PyGILState_STATE gstate;
    gstate = PyGILState_Ensure();

    // Get args, etc.

    // Call your Python function object
    PyObject * pInstance = PyObject_CallObject(pFunc, args);

    // Do any other needed Python API operations

    // Release the thread. No Python API allowed beyond this point.
    PyGILState_Release(gstate);
}
Run Code Online (Sandbox Code Playgroud)

此外,在扩展模块的init函数中,您应该执行以下操作以确保正确初始化线程:

// Make sure the GIL has been created since we need to acquire it in our
// callback to safely call into the python application.
if (! PyEval_ThreadsInitialized()) {
    PyEval_InitThreads();
}
Run Code Online (Sandbox Code Playgroud)

否则,当您尝试从非Python线程获取GIL时,可能会发生崩溃和奇怪的行为.

有关此内容的更多详细信息,请参阅非Python创建的线程.