从C线程调用Python代码

当从C(或C ++)线程调用Python代码时,我对如何确保线程安全性感到非常困惑。

Python文档似乎在说,通常这样做的习惯是:

PyGILState_STATE gstate; gstate = PyGILState_Ensure(); /* Perform Python actions here. */ result = CallSomeFunction(); /* evaluate result or handle exception */ /* Release the thread. No Python API allowed beyond this point. */ PyGILState_Release(gstate); 

事实上,这个stackoverflow答案似乎也证实了这一点。 但是一位评论者(声誉很高)却说不然。 评论者说你应该使用PyEval_RestoreThread() / PyEval_SaveThread()

文档似乎证实了这一点:

 PyThreadState* PyEval_SaveThread() Release the global interpreter lock (if it has been created and thread support is enabled) and reset the thread state to NULL, returning the previous thread state (which is not NULL). If the lock has been created, the current thread must have acquired it. (This function is available even when thread support is disabled at compile time.) void PyEval_RestoreThread(PyThreadState *tstate) Acquire the global interpreter lock (if it has been created and thread support is enabled) and set the thread state to tstate, which must not be NULL. If the lock has been created, the current thread must not have acquired it, otherwise deadlock ensues. (This function is available even when thread support is disabled at compile time.) 

文档描述的方式,似乎PyEval_RestoreThread() / PyEval_SaveThread()基本上是一个互斥锁定/解锁成语。 因此,在从C调用任何Python代码之前,首先需要锁定GIL,然后解锁它。

那是哪个呢? 从C调用Python代码时,我应该使用:

PyGILState_Ensure()/PyGILState_Release()

要么

PyEval_RestoreThread/PyEval_SaveThread

真正的区别是什么?

首先,您几乎不想调用PyEval_RestoreThread / PyEval_SaveThread 。 相反,您希望调用包装器宏Py_BEGIN_ALLOW_THREADS / Py_END_ALLOW_THREADS 。 文档是为那些宏编写的,这就是为什么你找不到它。

无论如何,无论如何,你不使用线程函数/宏来获取GIL; 当你获得它时,你用它们暂时释放GIL。

那么,你为什么要这样做呢? 嗯,在简单的情况下,你没有; 你只需要Ensure / Release 。 但有时你需要保持你的Python线程状态直到以后,但不需要保持GIL(或者甚至明确需要保持GIL,允许其他一些线程进展,以便它可以发出信号)。 正如文档所解释的那样,最常见的原因是进行文件I / O或广泛的CPU绑定计算。

最后,是否有任何情况需要调用函数而不是宏? 是的,如果你想访问隐藏的PyThreadState。 如果你想不出你可能想要的原因,你可能没有。