在从C(或C ++)线程调用Python代码时,我对如何确保线程安全性感到非常困惑。
Python documentation似乎在说这样做的惯常做法是:
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 answer似乎也证实了这一点。但是一位评论员(声誉很高)却说不然。评论者说您应该使用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
?
那真正的区别是什么?
答案 0 :(得分:6)
首先,您几乎不想打电话给PyEval_RestoreThread
/ PyEval_SaveThread
。相反,您想要调用包装器宏Py_BEGIN_ALLOW_THREADS
/ Py_END_ALLOW_THREADS
。 documentation是为那些宏编写的,这就是为什么你找不到它。
无论如何,无论如何,你不使用线程函数/宏来获取GIL;当你获得它时,你用它们暂时释放GIL。
那么,你为什么要这样做呢?嗯,在简单的情况下,你没有;您只需要Ensure
/ Release
。但有时你需要保持你的Python线程状态直到以后,但不需要保持GIL(甚至明确需要不保持GIL,以允许其他一些线程进展所以它可以发出信号)。正如文档所解释的那样,最常见的原因是进行文件I / O或大量的CPU绑定计算。
最后,是否有任何情况需要调用函数而不是宏?是的,如果你想访问隐藏的PyThreadState。如果你想不出你可能想要的原因,你可能没有。