我在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还是连续调用?或者也许是一种解决这个线程问题的方法?
答案 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让我指向了正确的方向。