- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
基本上,对于 PyEval_InitThreads()
的确切时间,似乎存在大量混淆/歧义。应该被调用,并且需要什么伴随的 API 调用。 official Python documentation不幸的是非常模棱两可。已经有many questions on stackoverflow关于这个话题,事实上,我个人已经asked a question almost identical对于这个,所以如果它作为副本关闭,我不会特别惊讶;但考虑到这个问题似乎没有明确的答案。 (遗憾的是,我没有快速拨号的 Guido Van Rossum。)
首先,让我们在这里定义问题的范围:我想做什么?嗯...我想用 C 编写一个 Python 扩展模块,它将:
pthread
API 生成工作线程好的,让我们从 Python 文档本身开始。 Python 3.2 docs说:
void PyEval_InitThreads()
Initialize and acquire the global interpreter lock. It should be called in the main thread before creating a second thread or engaging in any other thread operations such as PyEval_ReleaseThread(tstate). It is not needed before calling PyEval_SaveThread() or PyEval_RestoreThread().
所以我的理解是:
PyEval_InitThreads()
在任何其他线程之前从主线程产生了PyEval_InitThreads
锁定 GIL所以常识告诉我们,任何创建线程的C扩展模块都必须调用PyEval_InitThreads()
,然后释放全局解释器锁。好吧,看起来很简单。所以初步看来,所需要的只是以下代码:
PyEval_InitThreads(); /* initialize threading and acquire GIL */
PyEval_ReleaseLock(); /* Release GIL */
看起来很简单...但不幸的是,Python 3.2 文档也说 PyEval_ReleaseLock
has been deprecated .相反,我们应该使用 PyEval_SaveThread
为了释放 GIL:
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.
呃...好吧,所以我猜一个 C 扩展模块需要说:
PyEval_InitThreads();
PyThreadState* st = PyEval_SaveThread();
确实,这正是 this stackoverflow answer说。除非我在实践中实际尝试,否则当我导入扩展模块时,Python 解释器会立即出现段错误。好的。
好的,所以现在我要放弃官方 Python 文档并转向 Google。所以, this random blog 声称您需要从扩展模块中调用 PyEval_InitThreads()
。当然,文档声称 PyEval_InitThreads()
获得了 GIL,实际上是 quick inspection of the source code for PyEval_InitThreads()
in ceval.c
显示它确实调用了内部函数 take_gil(PyThreadState_GET());
所以 PyEval_InitThreads()
肯定 获得了 GIL。我会认为你绝对需要在调用 PyEval_InitThreads()
之后以某种方式释放 GIL。 但是如何? PyEval_ReleaseLock()
已被弃用,而 PyEval_SaveThread()
只是莫名其妙的段错误。
好的...所以可能出于某种目前我无法理解的原因,C 扩展模块不需要 需要释放 GIL。我试过了……正如预期的那样,一旦另一个线程尝试获取 GIL(使用 PyGILState_Ensure ),程序就会因死锁而挂起。所以是的......你真的需要在调用 PyEval_InitThreads()
之后释放 GIL。
同样,问题是:在调用 PyEval_InitThreads()
后如何释放 GIL? ?
更一般地说:C 扩展模块究竟需要做什么才能安全地从工作 C 线程调用 Python 代码?
最佳答案
您的理解是正确的:调用 PyEval_InitThreads
确实会获取 GIL。在正确编写的 Python/C 应用程序中,这不是问题,因为 GIL 会及时解锁,无论是自动还是手动。
如果主线程继续运行 Python 代码,没有什么特别的,因为 Python 解释器会在执行了一些指令后自动放弃 GIL(允许另一个线程获取它,这将再次放弃它) , 等等)。此外,每当 Python 即将调用阻塞系统调用时,例如从网络读取或写入文件,它会在调用周围释放 GIL。
这个答案的原始版本几乎到此结束。但还有一件事需要考虑:嵌入场景。
在嵌入 Python 时,主线程通常会初始化 Python 并继续执行其他与 Python 无关的任务。在那种情况下,没有什么会自动释放 GIL,所以这必须由线程本身来完成。这绝不是特定于调用 PyEval_InitThreads
的调用,它应该是 all Python/C code使用获取的 GIL 调用。
例如,main()
可能包含如下代码:
Py_Initialize();
PyEval_InitThreads();
Py_BEGIN_ALLOW_THREADS
... call the non-Python part of the application here ...
Py_END_ALLOW_THREADS
Py_Finalize();
如果你的代码手动创建线程,他们需要在做任何与 Python 相关的任何事情之前获取 GIL,甚至像 Py_INCREF
这样简单。为此,请使用 the following :
// Acquire the GIL
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
... call Python code here ...
// Release the GIL. No Python API allowed beyond this point.
PyGILState_Release(gstate);
关于python - Python 3 : How/when to call it? 中的 PyEval_InitThreads(传奇继续令人作呕),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15470367/
我是一名优秀的程序员,十分优秀!