我将python解释器嵌入到一个多线程的C应用程序中,对于应该使用什么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()
调用中很快就死锁了。
那么我在这里错过了什么呢?
发布于 2012-05-22 20:33:11
最终我发现了这一点。
之后
PyEval_InitThreads();
你需要打电话给
PyEval_SaveThread();
同时适当地释放主线程的GIL。
发布于 2014-01-26 23:56:23
我遇到了完全相同的问题,现在可以按照您上面的建议,在PyEval_InitThreads()
之后立即使用PyEval_SaveThread()
来解决这个问题。然而,我的实际问题是,我在PyInitialise()
之后使用PyEval_InitThreads()
,这会导致PyGILState_Ensure()
在从不同的后续本机线程调用时阻塞。总而言之,这是我现在要做的:
static int = 0;
Py_Initialize()
如果(! gil_init ) {gil_init= 1;PyEval_InitThreads();PyEval_SaveThread();}状态= PyGILState_Ensure();//调用Python/C API函数...PyGILState_Release(state);
Py_Finalize()
我尝试过的所有其他解决方案要么会导致随机的Python信号错误,要么会使用PyGILState_Ensure()
导致死锁/阻塞。
Python文档确实应该更清楚地说明这一点,并至少为嵌入和扩展用例提供一个示例。
发布于 2022-01-22 18:16:57
注意,@forman的答案中的if (!gil_init) {
代码只运行一次,所以它可以在主线程中很好地完成,这允许我们删除标志(gil_init
应该是原子的或以其他方式同步的)。
PyEval_InitThreads()
只有在CPython 3.6和更早的版本中才有意义,并且在CPython 3.9中已被弃用,因此必须用宏保护它。
考虑到所有这些,我目前使用的是:
在主线程中,运行
Py_Initialize();
PyEval_InitThreads(); // only on Python 3.6 or older!
/* tstate = */ PyEval_SaveThread(); // maybe save the return value if you need it later
现在,无论何时需要调用Python,都可以
state = PyGILState_Ensure();
// Call Python/C API functions...
PyGILState_Release(state);
最后,从主线程停止Python解释器
PyGILState_Ensure(); // PyEval_RestoreThread(tstate); seems to work just as well
Py_Finalize()
https://stackoverflow.com/questions/10625584
复制相似问题