不同线程上的Python回调& GIL

时间:2016-11-08 13:31:31

标签: python multithreading callback ctypes python-c-api

我在python模块中包装属于多线程C框架的C函数。在此框架中,存在在触发某些事件时执行的回调。但是,回调并不总是从同一个线程执行。

与我的问题相关的回调在python ctypes中定义。普通ctypes回调的问题是它们试图使用python C-API获取GIL。从没有调用int的线程运行时,此采样失败。

由于框架我不能改变回调的执行方式,我唯一能做的就是改变回调函数本身。

要启用对GIL获取方式的控制,我已经创建了一个包含python回调函数的C回调函数:

Py_initialize()

如果void* callback(void*){ if (PyGILState_GetThisThreadState()) { PyGILState_STATE gstate; gstate = PyGILState_Ensure(); PyRun_SimpleString(...); PyGILState_Release(gstate); } else { PyRun_SimpleString(...); } } 函数返回非NULL值,则表示从调用PyGILState_GetThisThreadState()的同一线程执行回调。这意味着可以使用PyGILState API。这工作正常。

当从其他线程运行回调时执行Py_initialize()部分。如果它是该线程堆栈上唯一的python调用,它将被正确执行,但是当堆栈中存在未完成的python调用时,将导致分段错误:

else

我已经尝试过将PyGILState API用于else部分,但是它会导致相反的情况:连续的python调用回调成功,但是当它是堆栈中的第一个python调用时会导致死锁:

#0  0x0000003c46ce6cdb in PyImport_GetModuleDict () from /usr/lib64/libpython2.6.so.1.0
#1  0x0000003c46ce6d2b in PyImport_AddModule () from /usr/lib64/libpython2.6.so.1.0`
#2  0x0000003c46cf2fe8 in PyRun_SimpleStringFlags () from /usr/lib64/libpython2.6.so.1.0
...
<framework calls>
...
#19 0x0000003c46cf1daa in PyRun_StringFlags () from /usr/lib64/libpython2.6.so.1.0
#20 0x0000003c46cf3010 in PyRun_SimpleStringFlags () from /usr/lib64/libpython2.6.so.1.0
#21 0x00007fc72c78dbb6 in execString () from ...

如果我以与#0 0x0000003c8de0da00 in sem_wait () from /lib64/libpthread.so.0 #1 0x0000003c46cfd428 in PyThread_acquire_lock () from /usr/lib64/libpython2.6.so.1.0 #2 0x0000003c46cd7784 in PyEval_RestoreThread () from /usr/lib64/libpython2.6.so.1.0 #3 0x0000003c46cf0f58 in PyGILState_Ensure () from /usr/lib64/libpython2.6.so.1.0 部分相同的方式获得GIL状态,使用if(PyGILState_GetThisThreadState()){..}PyGILState_Ensure,它将在堆栈中存在较早的python调用时正常工作,但是当堆栈中没有python调用时则不行。

有没有办法确定回调是要进行第一次python还是连续调用?或者也许是一种解决这个线程问题的方法?

1 个答案:

答案 0 :(得分:0)

显然我可以使用_PyThreadState_Current,我仍然不能100%确定为什么会这样,但确实如此。它似乎应该是currentThread而不是!currentThread

void callback(){
    PyThreadState * currentThread = _PyThreadState_Current;
    if (PyGILState_GetThisThreadState() || !currentThread){
        PyGILState_STATE gstate;
        gstate = PyGILState_Ensure();
        PyRun_SimpleString(....);
        PyGILState_Release(gstate);
    }else{
        PyRun_SimpleString(....);
    }

感谢this post让我指向了正确的方向。