我写了一个小模块,它使用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()
答案 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锁定。