Jus*_*ore 5 c python python-embedding python-3.x
Python 的弃用PyEval_ReleaseLock在我们的代码库中引入了一个问题:我们希望使用 C 回调函数终止 Python 解释器Py_EndInterpreter
所以要做到这一点,Python 的 Docs说在调用这个函数时你必须持有 GIL:
void 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.
Run Code Online (Sandbox Code Playgroud)
的文档PyEval_ReleaseLock说我们应该使用PyEval_SaveThread或PyEval_ReleaseThread。
PyEval_ReleaseThread的文档说输入线程状态不能为 NULL。好的,但是我们不能传入最近删除的线程状态。
PyEval_SaveThread如果您在调用 之后尝试调用它,则会遇到调试断言Py_EndInterpreter,因此这也不是一个选项。
因此,我们目前已经实施了一个 hack 来解决这个问题——我们将调用的线程的线程状态保存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.
Run Code Online (Sandbox Code Playgroud)
这里的正确解决方案是什么?似乎嵌入式 Python 是此 API 的事后想法。