Python C 扩展是否像为 IO 任务一样为 CPU 任务发布 GIL?

Do Python C Extensions release the GIL for CPU tasks as they do for IO tasks?

Python C 扩展总是在进行 IO 系统调用时通过 Py_BEGIN_ALLOW_THREADS 释放 GIL。关于 C Extensions 是否为相对较长的 运行 CPU 绑定任务发布 GIL,我已经阅读了不同的意见。我读过 python 解释器中的 CPU 受 GIL 约束,但 C 扩展中的 CPU 通常会释放 GIL。我认为这样做是安全和高效的,只要 CPU 任务可以在具有细粒度锁定的临时变量中执行,并且在更新共享结构之前获取 GIL。

我在 Github 中搜索了 3.6 C 代码以查找 Py_BEGIN_ALLOW_THREADS 的所有实例。我可能错过了一些,但在我看来 Python C 扩展通常不会为 CPU 绑定任务发布 GIL。

Python C 扩展通常会像为 IO 任务一样为 CPU 任务释放 GIL?


这是我看到的他们为 CPU 任务发布 GIL 的唯一示例:

_hashlib.h 有一个宏 ENTER_HASHLIB,其注释表明它将在 CPU 消耗 hashlib 操作时释放 GIL。

sha3module.c 似乎在 CPU 操作之前允许线程。

_lzmamodule.c 似乎在 CPU 操作之前允许线程。


我找不到任何其他 CPU 释放 GIL 的调用(我肯定可能错过了一些,如果我错过了请告诉我)。

例如:

_bisectmoduel.c没有Py_BEGIN_ALLOW_THREADS.

_heapqmodule.c没有Py_BEGIN_ALLOW_THREADS.

_json.c没有Py_BEGIN_ALLOW_THREADS.

_csv.c没有Py_BEGIN_ALLOW_THREADS.

好吧,numpy 做到了,尽管他们用自己的宏包裹它 (NPY_BEGIN_ALLOW_THREADS / NPY_END_ALLOW_THREADS)

根据docs

This group is used to call code that may take some time but does not use any Python C-API calls. Thus, the GIL should be released during its calculation.

所以你可以释放GIL,但只有如果你不使用任何Python C-API调用。另一种说法是 if you do not access Python objects.

但是您链接的最后四个扩展模块中的代码填充 有 C-API 调用。因此,在那些不涉及 C-API 调用的模块中,可能没有用 C 完成足够的计算,因此值得释放和重新获取 GIL。