1

我正在创建一个 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)

这是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()
4

1 回答 1

1

不知道为什么我以前没有抓住这个。如果要将函数对象作为参数传递给线程,请这样做...

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

之前,我只是在该行代码中调用函数,而不是传递函数对象。

于 2014-02-27T01:57:58.470 回答