我正在创建一个 GTK/GStreamer 应用程序(用于音频的 C gstreamer 和用于 gui 的 pygobject gtk),它等待 MIDI 输入并对其做出响应。MIDI 捕获由 RTMidi 库处理。
过去,我能够在 Cython .pyx 文件中使用nogil和gil关键字成功发布 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)
这是pygobject gtk:
#!/usr/bin/python
from gi.repository import Gtk
import player
import threading
class MyWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Hello World")
self.player = player.PyPlayer()
def test():
print("hello, world!")
win = MyWindow()
#this way of starting the midi on a separate thread worked before,
#but not anymore
thread = threading.Thread(target=win.player.get_midi_in(test))
thread.daemon = True
thread.start()
win.connect("delete-event", win.close_app)
win.show_all()
Gtk.main()