在Python的不同线程上运行导出的C ++函数时出现问题

时间:2014-01-22 22:43:20

标签: python cython python-multithreading

我写了一个小模块,它使用RtMidi库从MIDI设备接受MIDI输入: http://www.music.mcgill.ca/~gary/rtmidi/

我使用该库没有问题,它的效果很好。我已经使用Cython导出了我的模块以供Python使用。该模块导入正常,并与Python正常工作。问题是,由于这会无限期地等待用户MIDI输入,因此有必要在自己的线程中生成它。

我已经测试过使用std :: thread在C ++中自己的线程中生成它,它运行得很好。问题是在使用导出的库时,特别试图在Python内部自己的线程上生成它。

以下是代码:

    import midi   #my exported C++ module
    import threading
    import time

    def test():
      for i in range(0, 10):
        print(i)
        time.sleep(.25)

    my_thread = threading.Thread(target=test)
    my_thread.daemon = False
    my_thread.start()

    midi_input = midi.MidiListen()
    midi_input.start()   #listen for MIDI input indefinitely

发生的事情是我生成的线程将打印'0',然后MIDI输入阅读器将启动,并且第一个线程将不会继续打印,直到midi_input.start()函数返回,而不是两个函数同时运行。

此外,我尝试以另一种方式运行此代码,在单独的线程而不是test()函数上执行midi_input.start()。

我在这里做错了什么?

修改 由于杰夫的回答,我能够解决我的问题。

注意函数声明末尾的“nogil”和“with gil”。

这是我的.pyx文件:

from libcpp.vector cimport vector

cdef extern from "hello_midi.h":
  cdef cppclass MidiInput:
      void mycallback(double deltatime, vector[unsigned char]* message, void* userData) with gil
      int midi_listen() nogil

cdef void midi_init(MidiInput* ob):
  with nogil:
      ob.midi_listen()

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

这是最终的Python脚本:

import midi
import threading
import time


def test():
    for i in range(0, 10):
        print(i)
        time.sleep(.25)

def start():
    x = midi.MidiListen()
    x.start_midi()

thread = threading.Thread(target=start)
thread.start()

test()

1 个答案:

答案 0 :(得分:3)

这是因为Python的全局解释器锁(GIL)。一次只允许一个线程在Python解释器中运行。要解决您的问题,您应该在调用RtMidi之前释放GIL。 Cython手册在Acquiring and Releasing the GIL部分讨论了这一点。

您的新代码看起来像

def start(self):
    with nogil:
        # Call rtmidi functions

但是,如果要操作任何Python对象,则必须获取GIL,例如在回调中。看起来RtMidi是面向回调的,所以如果你现在有

cdef void callback(...):
    # Code that posts the midi event to python somehow

你应该把它改成

cdef void callback(...) with gil:
    # The same code as before

这会导致Cython发出代码,在运行函数体之前获取GIL锁定。