Python 使用基于回调的C库时,无法使用Cython释放GIL

Python 使用基于回调的C库时,无法使用Cython释放GIL,python,callback,cython,pygobject,gil,Python,Callback,Cython,Pygobject,Gil,我正在创建一个GTK/GStreamer应用程序(用于音频的C GStreamer和用于gui的pygobject GTK),它等待MIDI输入并响应它。MIDI捕获由RTMidi库处理 过去,我能够在Cython.pyx文件中使用nogil和GIL关键字成功地发布GIL。问题是,我需要能够从python的C回调函数中读取MIDI输入值,以便能够基于此输入对GUI进行更改 因此,我稍微重新构造了代码,以便python代码传递一个python函数对象(我想在RTMidi C回调中调用该函数),然后

我正在创建一个GTK/GStreamer应用程序(用于音频的C GStreamer和用于gui的pygobject GTK),它等待MIDI输入并响应它。MIDI捕获由RTMidi库处理

过去,我能够在Cython.pyx文件中使用nogilGIL关键字成功地发布GIL。问题是,我需要能够从python的C回调函数中读取MIDI输入值,以便能够基于此输入对GUI进行更改

因此,我稍微重新构造了代码,以便python代码传递一个python函数对象(我想在RTMidi C回调中调用该函数),然后使用Cython将其转换为void*类型的对象,以便通过C代码传递该数据

换句话说,每当检测到MIDI输入时,就会调用作为void*传递的python函数。这个很好用。自从我添加了这个功能后,没有工作的部分是GIL的发布。现在,每当我的应用程序启动时,我接受MIDI输入的函数都没有在自己的线程上正确启动,因此GTK gui从未实际显示

关于在Cython文档中发布GIL的一些信息:

语句体中的代码不得以任何方式操纵Python对象,也不得在未首先重新获取GIL的情况下调用任何操纵Python对象的代码。Cython目前未对此进行检查。

我不认为我违反了这些规则中的任何一条

这可能是Cython的错误还是我遗漏了什么

其他人有没有使用Cython从C回调调用python函数的经验

这里是.pyx的简化版本。注意用于发布和获取gil的“with gil”和“nogil”关键字:

from libcpp.vector cimport vector

cdef extern from "player.h":
    cdef cppclass Player:
        Player()
        void listen(void(*)(double, vector[unsigned char]*, void*), void* userdata) nogil

cdef extern from "midi.h":
    cdef cppclass MidiInput:
        int midi_listen(void(*)(double, vector[unsigned char]*, void*), void* userdata) nogil

cdef void midi_init(Player* ob, void* function):
    with nogil:
        ob.listen(callback, function)

#helper function which the RTMidi callback is set to
#the python function is called from the callback
cdef void callback(double deltatime, vector[unsigned char]* message, void* userdata) with gil:
    (<object>userdata)()    

cdef class MidiListen:
    cdef MidiInput* thisptr
    def __cinit__(self):
        self.thisptr = new MidiInput()

cdef class PyPlayer:
    cdef Player *thisptr
    def __cinit__(self):
        self.thisptr = new Player()
    def get_midi_in(self, f):
        midi_init(self.thisptr, <void*>f)

不知道为什么我以前没听清楚。如果要将函数对象作为参数传递给线程,请按如下方式执行

thread = threading.Thread(target=win.player.get_midi_in, args=(test,))
以前,我只是在那行代码中调用函数,而没有传递函数对象

thread = threading.Thread(target=win.player.get_midi_in, args=(test,))