pygtk GUI因pyjack线程而冻结
我有一个程序,可以通过火线设备(FA-66)录音,使用的是Jack连接。这个程序的界面是用pygtk做的,录音则是用py-jack来实现的(可以在这里找到:http://sourceforge.net/projects/py-jack/)。录音是在一个单独的线程中进行的,因为我需要同时使用图形界面来查看音频的录制结果。
问题是,当我启动录音线程时,图形界面的反应变得非常慢。我在主线程的开头调用了gtk.gdk的start_threads()函数。如果我理解得没错,我不需要使用threads_enter()和threads_leave(),因为录音并不会影响到图形界面。如果我错了,请纠正我。
jack.process()这个函数是用来从三个麦克风录音的。如果我把它换成比如time.sleep(2),那么一切就正常了。
在这种情况下,创建线程的最佳方式是什么呢?为什么jack.process会让图形界面卡住?它是不是占用了所有的CPU时间?下面是我代码的一些示例:
soundrecorder.py:
...
def start(self):
Thread(target=self._start).start()
def _start(self):
while self.eventhandler.record.isSet():
data = self._jackRecord(self.sample_length)
self.datahandler.queue.put(data)
def _jackRecord(self, length):
capture = Numeric.zeros((self.inputs, int(self.sample_rate * length)), 'f')
output = Numeric.zeros((self.inputs, self.buffer_size), 'f')
i = 0
while i < capture.shape[1] - self.buffer_size:
try:
jack.process(output, capture[:,i:i+self.buffer_size])
i += self.buffer_size
except:
pass
return capture
eventhandler.py: recordStart()和recordStop()只是简单的回调函数,当按下开始和停止按钮时会被调用。
...
def recordStart(self, widget, data=None):
if not self.record.isSet():
self.record.set()
self.soundrecorder = SoundRecorder(self, self.datahandler)
self.soundrecorder.connect()
self.soundrecorder.start()
def recordStop(self, widget, data=None):
if self.record.isSet():
self.record.clear()
del(self.soundrecorder)
1 个回答
你对线程的工作原理有些误解。
在这种情况下,线程并不能帮你。
"当一个样本被记录时,它会被分析,结果会在图形界面上显示。同时,下一个样本已经在录制中。"
错了。线程并不能同时做两件事。在Python中,有一个全局锁,防止两个线程同时运行Python代码或操作Python对象。而且,如果你没有两个CPU或核心,实际上也不会有两件事同时发生。线程机制只是让它们在执行时切换,每次执行固定数量的指令。
使用线程还会增加处理、内存和代码的复杂性,但没有任何好处。使用线程的Python代码运行得更慢,性能也更低,比起单线程来说。只有少数情况例外,而你的情况并不在其中。
你可能想把你的录制循环改写成一个回调,并将其与GTK循环整合(这样性能会比使用线程更好)。
为此,可以使用一个优先级较高的gobject.idle_add
。
如果你想真正做到“两件事同时进行”,也就是使用两个处理器/核心,你需要启动另一个进程。启动一个进程来收集数据,并通过某种进程间通信机制将数据传输到另一个进程,这个进程负责分析和绘制数据。multiprocessing模块可以帮助你实现这一点。