10

当从 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?


真正的区别是什么?

4

1 回答 1

6

首先,您几乎不想调用PyEval_RestoreThread/ PyEval_SaveThread。相反,您想调用包装宏Py_BEGIN_ALLOW_THREADS/ Py_END_ALLOW_THREADS。该文档是为这些宏编写的,这就是您找不到它的原因。

无论如何,无论哪种方式,您都不要使用线程函数/宏来获取 GIL;获得 GIL 后,您可以使用它们临时释放 GIL。

那么,你为什么要这样做呢?好吧,在简单的情况下,您不会;你只需要Ensure/ Release。但有时您需要保留 Python 线程状态直到稍后,但不需要保留 GIL(甚至明确不需要保留GIL,以允许其他线程继续运行,以便它可以向您发出信号)。正如文档所解释的,最常见的原因是执行文件 I/O 或大量 CPU 密集型计算。

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

于 2012-09-14T22:24:43.320 回答