我在多线程C应用程序中嵌入了python解释器,我对使用哪些API来确保线程安全感到有点困惑。
从我收集到的内容中,嵌入python时,在调用任何其他Python C API调用之前,由嵌入器来处理GIL锁。这是通过以下功能完成的:
gstate = PyGILState_Ensure();
// do some python api calls, run python scripts
PyGILState_Release(gstate);
但仅此一点似乎还不够。我仍然有随机崩溃,因为它似乎没有为Python API提供互斥。
在阅读了更多文档后,我还补充说:
PyEval_InitThreads();
在致电Py_IsInitialized()
之后,但这就是令人困惑的部分。文档说明了这个函数:
初始化并获取全局解释器锁
这表明当此函数返回时,GIL应该被锁定并且应该以某种方式解锁。但实际上这似乎并不是必需的。有了这条线,我的多线程工作完美,并且PyGILState_Ensure/Release
功能维持了互斥
当我在PyEval_ReleaseLock()
之后尝试添加PyEval_ReleaseLock()
时,该应用会在随后的PyImport_ExecCodeModule()
调用中很快被锁定。
那我在这里错过了什么?
答案 0 :(得分:7)
我有完全相同的问题,现在可以在PyEval_SaveThread()
之后立即使用PyEval_InitThreads()
来解决,如上所述。但是,我的实际问题是我在PyEval_InitThreads()
之后使用PyInitialise()
,然后在从不同的后续本机线程调用时导致PyGILState_Ensure()
阻塞。总之,这就是我现在所做的:
有全局变量:
static int gil_init = 0;
从主线程加载本机C扩展并启动Python解释器:
Py_Initialize()
我的应用程序从多个其他线程同时对Python / C API进行大量调用:
if (!gil_init) {
gil_init = 1;
PyEval_InitThreads();
PyEval_SaveThread();
}
state = PyGILState_Ensure();
// Call Python/C API functions...
PyGILState_Release(state);
从主线程停止Python解释器
Py_Finalize()
我尝试过的所有其他解决方案都会导致使用PyGILState_Ensure()
的随机Python sigfaults或死锁/阻塞。
Python文档确实应该更清楚,至少为嵌入和扩展用例提供了一个示例。
答案 1 :(得分:4)
最终我明白了。
之后
PyEval_InitThreads();
您需要致电
PyEval_SaveThread();
正确释放主线程的GIL。
答案 2 :(得分:-1)
让多线程C应用程序尝试从多个线程与单个CPython实例的多个Python线程进行通信对我来说是冒险的。
只要只有一个C线程与Python通信,即使Python应用程序是多线程的,也不必担心锁定。 如果你需要多个python线程,你可以用这种方式设置应用程序,并让多个C线程通过队列与那个将它们运行到多个Python线程的单个C线程进行通信。
可能对您有用的另一种方法是为每个需要它的C线程提供一个CPython实例(当然,Python程序之间的通信应该通过C程序)。
另一种替代方案可能是Stackless Python解释器。这消除了GIL,但我不确定你遇到其他问题将它绑定到多个线程。 stackless是我(单线程)C应用程序的替代品。