我正在将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()
的调用中很快就会出现死锁。
那么我在这里错过了什么?
PyEval_SaveThread
应该始终与PyEval_RestoreThread
结合使用。正如其他地方所解释的,在初始化锁之后不应尝试释放锁;只需让 Python 在其常规工作的一部分中释放它即可。 - user4815162342PyEval_SaveThread();
,则主线程将阻止其他线程访问Python。换句话说,PyGILState_Ensure()
会发生死锁。 - khkarensPyEval_SaveThread()
必须由调用PyEval_InitThreads()
的线程调用,否则当线程尝试调用PyGILState_Ensure()
时会发生死锁(因为 GIL 不可用于检索)。PyEval_RestoreThread()
应该最终由调用PyEval_SaveThread()
的同一线程调用,但此时重要的是所有可能调用PyGILState_Ensure()
的线程都已完成,否则可能会出现死锁,原因相同。 - andreasdr