不同线程上的Python回调&GIL

2024-10-01 07:35:46 发布

您现在位置:Python中文网/ 问答频道 /正文

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

与我的问题相关的回调是在python ctypes中定义的。普通ctypes回调的问题是,它们试图使用pythoncapi获取GIL。当从未调用Py_initialize()的线程运行时,此获取失败。在

由于框架的原因,我无法更改回调的执行方式,我唯一能做的就是更改回调函数本身。在

为了控制获取GIL的方式,我创建了一个C回调函数,它包装了python回调函数:

void* callback(void*){
    if (PyGILState_GetThisThreadState()) {
        PyGILState_STATE gstate;
        gstate = PyGILState_Ensure();
        PyRun_SimpleString(...);
        PyGILState_Release(gstate);
    } else {
        PyRun_SimpleString(...);
    }
}

如果PyGILState_GetThisThreadState()函数返回非空值,则表示回调是从调用Py_initialize()的同一线程执行的。这意味着可以使用pygilstateapi。这工作正常。在

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

^{pr2}$

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

#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

如果我以与if(PyGILState_GetThisThreadState()){..}部分相同的方式获得GIL状态,使用PyGILState_Ensure和{},那么当堆栈中有一个早期的python调用时,它将正常工作,但当堆栈中没有python调用时则不行。在

有没有方法可以确定回调是要进行第一个python调用还是一个连续调用?或者是解决这个线程问题的方法?在


Tags: 函数infrom框架堆栈usr方式线程
1条回答
网友
1楼 · 发布于 2024-10-01 07:35:46

显然我可以使用_PyThreadState_Current,我仍然不能百分之百确定为什么这样做,但它确实起作用了。似乎应该是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为我指明了正确的方向。在

相关问题 更多 >