Python多处理,PyAudio和wxPython

时间:2014-09-19 01:06:18

标签: python wxpython multiprocessing pyaudio

我有一个wxPython GUI,并希望使用多处理来创建一个使用PyAudio的独立进程。也就是说,我想使用PyAudio,wxPython和多处理模块,但是虽然我可以使用其中任何两个,但我不能将这三个一起使用。具体来说,如果从一个文件导入wx,并创建一个打开PyAudio的multiprocessing.Process,PyAudio将无法打开。这是一个例子:

档案:A.py

import wx
import time
use_multiprocessing = True
if use_multiprocessing:
    from multiprocessing import Process as X
else:
    from threading import Thread as X
import B

if __name__=="__main__":
    p = X(target=B.worker)
    p.start()
    time.sleep(5.)
    p.join()

档案:B.py

import pyaudio

def worker():
    print "11"
    feed = pyaudio.PyAudio()
    print "22"
    feed.terminate()

在我的所有测试中,我看到11打印,但问题是我没有看到22所显示的程序。

  • 如果我只评论import wx,我会看到22和pyaudio加载
  • 如果我只设置use_multiprocessing=False,那么我使用线程代替,我看到22和pyaudio加载。
  • 如果我在worker中执行其他操作,它将会运行(只有pyaudio不会运行)

我用Python 2.6和2.7尝试过这个; PyAudio 0.2.4,0.2.7和0.2.8;和wx 3.0.0.0和2.8.12.1;我正在使用OSX 10.9.4

2 个答案:

答案 0 :(得分:3)

这有两个原因可以发生,但它们看起来几乎相同。

无论哪种方式,根本问题是multiprocessing只是fork一个孩子。这可能导致CoreFoundation对其runloop *感到困惑,或导致wx内部的某些内部对象对其线程感到困惑。**


但是你不在乎为什么你的孩子进程陷入僵局;你想知道如何解决它。

简单的解决方案是,而不是尝试fork,然后清理所有不应该被复制的东西,spawn一个全新的Python流程,然后复制所有应该的东西。

从Python 3.4开始,实际上有两种变体。有关详情,请参阅Contexts and start methods;有关背景信息,请参阅issue #8713

但你是2.6,所以这对你没有帮助。那么,你能做什么?


最简单的答案是从multiprocessing切换到第三方库billiardbilliard是Python 2.7的multiprocessing的分支,它添加了Python 3.x和Celery的许多功能和错误修复。

我相信新版本与Python 3.4具有完全相同的修复,但我不是正面的(对不起,我没有安装它,也无法在线找到文档...)。

但我确定它有一个类似但不同的解决方案,继承自Celery:在调用库中的任何其他内容之前调用billiards.forking_enable(False)。 (或者,从程序外部,设置环境变量MULTIPROCESSING_FORKING_DISABLE=1。)


*通常,CF可以检测到问题并调用__THE_PROCESS_HAS_FORKED_AND_YOU_CANNOT_USE_THIS_COREFOUNDATION_FUNCTIONALITY___YO‌U_MUST_EXEC__,它会记录错误消息并失败。但有时它不会,等待永远等待一个没人能发送的事件。谷歌的字符串以获取更多信息。

**有关与线程Tkinter等效问题的详细信息以及基础问题,请参阅#5527。这个会影响所有类似BSD的* nix,而不仅仅是OS X.

答案 1 :(得分:1)

如果您无法通过修复或解决multiprocessing来解决问题,还有另一种选择。如果您可以在创建主runloop或创建任何线程之前分离子进程,则可以防止子进程混淆。这总是不起作用,但常常,所以可能值得尝试。

在调用像mainloop这样的函数或构造App实例之前,使用Tkinter或PySide或其他实际上没有做任何事情的库很容易。

但是对于wx,我认为在你触摸import之外的任何东西之前它会进行一些设置。所以,你可能不得不做一些有点hacky的事情并在import wx之后移动p.start()

在您的真实应用中,您可能不希望在GUI发生某些触发之前开始播放音频。这意味着您需要创建某种同步对象,例如Event。因此,您创建Event,然后启动子进程。孩子初始化音频,然后等待Event。然后,在您想从GUI启动子项的地方,您只需发出Event的信号。