我有一个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
答案 0 :(得分:3)
这有两个原因可以发生,但它们看起来几乎相同。
无论哪种方式,根本问题是multiprocessing
只是fork
一个孩子。这可能导致CoreFoundation对其runloop *感到困惑,或导致wx
内部的某些内部对象对其线程感到困惑。**
但是你不在乎为什么你的孩子进程陷入僵局;你想知道如何解决它。
简单的解决方案是,而不是尝试fork
,然后清理所有不应该被复制的东西,spawn
一个全新的Python流程,然后复制所有应该的东西。
从Python 3.4开始,实际上有两种变体。有关详情,请参阅Contexts and start methods;有关背景信息,请参阅issue #8713。
但你是2.6,所以这对你没有帮助。那么,你能做什么?
最简单的答案是从multiprocessing
切换到第三方库billiard
。 billiard
是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___YOU_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
的信号。