1

问题

总结的问题是,当我使用 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 运行吗?到目前为止,我很困惑,我的应用程序或多或少没有功能。有任何想法吗?

4

0 回答 0