对Python PyEval_ReleaseLock
的弃用在我们的代码库中引入了一个问题:我们想使用Py_EndInterpreter
从C回调函数终止Python解释器
为此,Python's Docs说,在调用此函数时必须按住GIL:
无效Py_EndInterpreter(PyThreadState * tstate)
销毁由给定线程状态表示的(子)解释器。给定的线程状态必须为 当前线程状态。请参阅下面的线程状态讨论。通话返回时, 当前线程状态为NULL。与该解释器关联的所有线程状态均被销毁。 ( 全局解释器锁必须在调用此函数之前保持,并且在返回时仍保持。) Py_FinalizeEx()将销毁当时尚未明确销毁的所有子解释器。
太好了!因此,我们调用PyEval_RestoreThread
将线程状态恢复到将要终止的线程,然后调用Py_EndInterpreter
。
// Acquire the GIL
PyEval_RestoreThread(thread);
// Tear down the interpreter.
Py_EndInterpreter(thread);
// Now what? We still hold the GIL and we no longer have a valid thread state.
// Previously we did PyEval_ReleaseLock here, but that is now deprecated.
PyEval_ReleaseLock
的文档说我们应该使用PyEval_SaveThread
或PyEval_ReleaseThread
。
PyEval_ReleaseThread
的文档说输入线程的状态不能为NULL。好的,但是我们不能传递最近删除的线程状态。
PyEval_SaveThread
将在您调用Py_EndInterpreter
之后尝试调用调试断言,因此也不是选择。
因此,我们目前已实现了一种解决该问题的方法-我们将调用Py_InitializeEx
的线程的线程状态保存在全局变量中,并在调用Py_EndInterpreter
之后将其交换。
// Acquire the GIL
PyEval_RestoreThread(thread);
// Tear down the interpreter.
Py_EndInterpreter(thread);
// Swap to the main thread state.
PyThreadState_Swap(g_init.thread_state_);
PyEval_SaveThread(); // Release the GIL. Probably.
这里的正确解决方案是什么?似乎嵌入式Python是此API的事后建议。
相似的问题:PyEval_InitThreads in Python 3: How/when to call it? (the saga continues ad nauseam)