【问题标题】:unable to release GIL with Cython when using callback-based C libraries使用基于回调的 C 库时无法使用 Cython 发布 GIL
【发布时间】:2014-03-27 02:11:42
【问题描述】:

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

过去,我能够在 Cython .pyx 文件中使用 nogilwith 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()

【问题讨论】:

    标签: python callback cython pygobject gil


    【解决方案1】:

    不知道为什么我以前没有抓住这个。如果你想将一个函数对象作为参数传递给线程,就这样做吧……

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

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

    【讨论】:

      猜你喜欢
      • 2017-08-03
      • 2012-07-26
      • 1970-01-01
      • 2013-01-29
      • 2018-09-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多