我在C++中编写了一个Python扩展,包装了一个我不控制的第三方库。该库创建一个线程Python一无所知,并且从该线程调用一个C++回调,我将提供给库。我希望这个回调调用一个Python函数,但是我使用从文档中读取的方法得到了一个死锁。以下是我对这些的解释。在
void Wrapper::myCallback()
{
PyGILState_STATE gstate=PyGILState_Ensure();
PyObject *result=PyObject_CallMethod(_pyObj,"callback",nullptr);
if (result) Py_DECREF(result);
PyGILState_Release(gstate);
}
我的代码不做任何与线程相关的事情,尽管我已经尝试了很多其他的事情。例如,基于this,我尝试调用PyEval_InitThreads()
,但不清楚应该在哪里对扩展进行调用。我把它放进了PyMODINIT_FUNC
。这些尝试都会导致死锁、崩溃或来自Python的神秘致命错误,例如PyEval_ReleaseThread:error thread state。在
这是在安装了python3.6.1的Linux上。有什么办法让这个“简单”的回调生效吗?在
我没有意识到在另一个线程中,库处于busy/wait循环中,等待回调的线程。在gdb
,info threads
使这一点变得明显。我能看到的唯一解决方案是跳过那些特定的回调调用;考虑到busy/wait循环,我看不出一种方法来保证它们的安全。在这种情况下,这是可以接受的,这样做可以消除死锁。在
而且,在这之前我似乎还需要调用PyEval_InitThreads()
。在C++扩展中,不清楚应该在哪里进行。其中一个回复建议在Python中通过创建和删除一个throwaway threading.Thread
来间接地执行此操作。这似乎并没有修复它,而是触发了一个致命的Python错误:以_gil:NULL tstate为例,我认为这意味着仍然没有gil。根据this及其所指的问题,我的猜测是PyEval_InitThreads()
导致当前线程成为GIL的主线程。如果这个调用是在短暂的一次性线程中发出的,那么这可能是个问题。是的,我只是在猜测,希望你能给我一个解释。在
我是StAdvOp溢出的新手,但我最近几天一直在将Python嵌入到多线程C++系统中,并遇到了代码死锁的情况。以下是我用来确保线程安全的解决方案:
正在初始化.cpp文件中的布尔值和互斥体。在
我注意到,如果没有互斥体,PyGILState_Ensure()命令会导致线程死锁。同样,在另一个PySafeContext的expr中调用PySafeContext将导致线程在等待互斥时阻塞。在
使用这些函数,我相信您的回调函数如下所示:
^{pr2}$如果您不相信您的代码可能需要多次对Python的多线程调用,那么可以轻松地扩展宏并从类结构中取出静态变量。这就是我如何处理一个未知线程,启动并确定它是否需要启动系统,并避免重复编写GIL函数的单调乏味。在
希望这有帮助!在
这个答案只适用于Python>;=3.0.0。我不知道这对早期的Python是否有效。在
在类似于此的Python模块中封装C++模块:
从我对文档的阅读来看,这应该会强制在导入模块之前初始化线程。那么您在那里编写的回调函数应该可以工作了。在
我不太相信这一点,但您的module init函数可以这样做:
^{pr2}$这应该是可行的,因为如果
PyEval_ThreadsInitialized()
不是真的话,那么您的module init函数应该由唯一存在的Python线程执行,并且保持GIL是正确的做法。在这些都是我的猜测。我从来没有做过这样的事,我对你的问题毫无头绪的评论就证明了这一点。但从我对文档的阅读来看,这两种方法都应该有效。在
我用Python包了C++观察器。如果使用的是boost,那么可以在boost_PYTHON_模块中调用PyEval_InitThreads():
然后我用类来控制从C++调用回Python。在
^{pr2}$如果你调用C++的任何时间长度,你也可以放弃吉尔:
我们的代码是多线程的,这种方法工作得很好。在
相关问题 更多 >
编程相关推荐