问题
总结的问题是,当我使用 PyInstaller 打包我的应用程序时,我会左右抛出 MemoryErrors。我怀疑这与主要queue
对象没有被垃圾收集有关,但是,我在这里很黑暗。从源代码运行时它运行 100% OK,并且只占用几 MB 内存。它只有一次包装,它的气球失控。
计划概述
我有 GUI 应用程序,它提供桌面的图像转换(基本上是愚蠢的效果),并且(作为一个选项)将屏幕记录保存到磁盘。
由于 Python 的线程并不是真正并发的,因此程序的所有主要部分都运行在一个单独的multiprocessing
进程中,并通过共享Queue
对象进行通信。
基本设置是这样的:
它是基本的生产者/消费者。ScreenMonitor 生成屏幕快照,这些快照被推送到队列中,以便 ImageProcessor 可以使用它们。程序的大吞吐量点是queue
对象。几乎所有与程序相关的东西都会在那里传输——并且以内存攀升的速度,它必须在那里得到备份。
作为另一个数据点,程序在直接从源代码运行时运行 A-OK。即使有多个进程和繁重的图像处理,它也只占用几 MB 的内存。此外,我进行了长时间的 4-5 小时压力测试,以检查泄漏和不明显。所以,PyInstaller 一定很有趣。
从源代码运行
这是程序在全开运行时的内存占用,尽可能快地处理。
运行打包版本
那是在让它运行大约 30 秒之后。大约 3-4 分钟后,它会出现内存错误。
一些代码
注意:下面有大量注释,我取出了大量的程序簿,所以如果下面看起来有错误,或者变量不匹配,那是由于大量编辑造成的。我要说的不是生产代码:)
实际上,它的核心是一个非常基本的生产者/消费者模式。Process2
将图像数据放入队列,Process2
对图像进行操作。
class Grabber(NoDaemonProcess):
def __init__(self, in_queue, msg_queue):
multiprocessing.Process.__init__(self)
self.queue = in_queue
self.ext_msg_queue = msg_queue
self.settings = Settings()
self.count = 0
self.name == multiprocessing.current_process().name
self.running = True
def run(self):
start_time = time.time()
current = self.grab()
img_queue = multiprocessing.Queue()
image_processor = ImageProcess(img_queue, self.save_path)
image_processor.start()
clock = Clock()
time.clock()
while self.running:
buffer_type, content, times = self.get(current, img_queue)
self.img_queue.put((buffer_type, content, times))
class ImageProcess(NoDaemonProcess):
def __init__(self, queue, save_path):
multiprocessing.Process.__init__(self)
self.in_queue = queue
self.save_path = save_path
self.running = True
# self.daemon = False
def run(self):
out_queue = Queue.Queue()
threads = []
for i in range(5):
t = ImageSaver(out_queue)
t.setDaemon(True)
t.start()
threads.append(t)
# classmethod; effects all instances
ImageSaver.setSavePath(self.save_path)
while self.running:
queue_item = self.in_queue.get()
buffer_type, content, times = queue_item
# Do stuff with image stream..
所以!有谁之前经历过这个吗?PyInstaller 中有什么东西阻止了 GC 运行吗?到目前为止,我很困惑,我的应用程序或多或少没有功能。有任何想法吗?