如果没有 GIL,则不允许来自 Python 的强制转换

Coercion from Python not allowed without the GIL

我正在尝试从如下定义的 C++ 库中调用函数:

int somefunc(float timeout);

我在 pxd 文件中加倍了一个定义:

int somefunc(float timeout) nogil;

但是在对 pyx 文件进行 cythonizing 时,任何使用 nogil 调用该函数的尝试都会导致相同的错误:

 timeout = 1.0
 with nogil:
     Q.somefunc(timeout)
Error compiling Cython file:
------------------------------------------------------------
...
            int(block), timeout,
        )

        timeout = 1.0
        with nogil:
            Q.somefunc(timeout)
                      ^
------------------------------------------------------------

script.pyx:105:23: Coercion from Python not allowed without the GIL

我也试过用 ctype 调用它,这导致了同样的错误。

timeout = ctypes.c_float(1.0)
with nogil:
    Q.somefunc(timeout)

仅使用浮点文字有效。使用实际 Python 变量调用此函数的正确方法是什么?

当您查看为

生成的代码时
timeout = 1.0
Q.somefunc(timeout)

你会看到,timeoutPyObject 并且调用 __pyx_PyFloat_AsFloat 将 is 转换为 cdef-float。但是,在此转换期间可能会引发异常,为此需要 gil - 因此会出现错误消息。

解决方案是在 nogil-block 之前强制强制转换为 float

cdef float timeout = 1.0
with nogil:
    Q.somefunc(timeout)

现在超时已经是 cdef-float 并且在 nogil-block 中不需要转换。


稍微无关:我看不到代码和 Q 是什么,但大多数情况下让 C/C++ 代码完成工作时释放 gil 是错误的:

  1. 当 C/C++ 使用 openMP 进行并行化时,holding/not 持有 GIL 不会改变 C/C++ 代码的任何内容(这里是示例:).

  2. 当线程不持有 GIL 时,另一个线程可能会破坏 C/C++ 代码工作所需的对象(如 Q 或其他数据)

  3. 我们可以在使用实现缓冲区协议的对象时释放 GIL,因为缓冲区协议(正确实现时)会锁定缓冲区并且缓冲区不能从另一个 Python-Thread 销毁。其他 Python 对象没有这样的内置锁定。

但如上所述,它可能不适用于您的代码。