Python多进程内存增加

Python多进程内存增加,python,memory,multiprocessing,Python,Memory,Multiprocessing,我有一个应该永远运行的程序。 以下是我正在做的: from myfuncs import do, process class Worker(multiprocessing.Process): def __init__(self, lock): multiprocesing.Process.__init__(self) self.lock = lock self.queue = Redis(..) # this is a redis ba

我有一个应该永远运行的程序。 以下是我正在做的:

from myfuncs import do, process

class Worker(multiprocessing.Process):

    def __init__(self, lock):
        multiprocesing.Process.__init__(self)
        self.lock = lock
        self.queue = Redis(..) # this is a redis based queue
        self.res_queue = Redis(...)

     def run():
         while True:
             job = self.queue.get(block=True)
             job.results = process(job)
             with self.lock:
                 post_process(self.res_queue, job)


def main():
    lock = multiprocessing.Semaphore(1)
    ps = [Worker(lock) for _ in xrange(4)]
    [p.start() for p in ps]
    [p.join() for p in ps]
self.queue和self.res_queue是两个工作方式类似于python stdlib queue的对象,但它们 使用Redis数据库作为后端

函数进程对作业携带并返回的数据进行一些处理(主要是html解析) 一本字典

函数post_process通过检查某些条件将作业写入另一个redis队列(一次只有一个进程可以检查这些条件,这就是锁被锁定的原因)。它返回True/False

程序每天使用的内存正在增加。 有人能知道发生了什么事吗

当作业超出run方法中的作用域时,内存应该是空闲的,对吗

当作业超出run方法中的作用域时,内存应该是空闲的,对吗

首先,作用域是整个
run
方法,它永远循环,所以永远不会发生。(此外,当您退出
run
方法时,进程将关闭,其内存将被释放…)

但即使它确实超出了范围,这也不是你认为它的意思。Python不像C++,其中有变量的存储在堆栈上。所有对象都存在于堆上,并且在不再有对它们的引用之前,它们保持活动状态。变量超出范围意味着该变量不再引用它以前引用的任何对象。如果该变量是对对象的唯一引用,那么它将被释放*,但如果您在其他地方进行了其他引用,则在这些其他引用消失之前,无法释放该对象

同时,超出范围并没有什么神奇之处。变量停止引用对象的任何方式都具有相同的效果,无论是超出范围的变量、对其调用
del
、还是为其指定新值。因此,每次通过循环,当您执行
job=
时,您都会删除对
job
的先前引用,即使没有任何内容超出范围。(但请记住,在峰值时,您将有两个作业处于活动状态,而不是一个,因为在释放旧作业之前,新作业已从队列中拉出。如果这是一个问题,您可以在阻塞队列之前始终执行
job=None
。)

因此,假设问题实际上是
作业
对象(或它所拥有的东西),问题是您没有向我们展示的一些代码在某处保留了对它的引用

在不知道自己在做什么的情况下,很难提出解决方案。它可能只是“不要在那里存储”。或者它可能是“存储一个weakref而不是对象本身”。或“添加LRU算法”。或者“添加一些流控制,这样如果备份太多,在内存耗尽之前就不会继续堆积工作”



*在CPython中,这会立即发生,因为垃圾收集器基于refcounting。另一方面,在Jython和IronPython中,垃圾收集器只依赖于底层VM的垃圾收集器,因此在JVM或CLR注意到不再引用对象之前,对象不会被释放,这通常不是即时的,并且是不确定的。

如果找不到泄漏源,您可以通过让每个员工只处理有限数量的任务来解决此问题。一旦它们达到任务限制,您就可以让它们退出,并用新的辅助进程替换它们。内置的
多处理.Pool
对象通过关键字参数支持这一点。你可以做类似的事情:

import multiprocessing
import threading

class WorkerPool(object):
    def __init__(self, workers=multiprocessing.cpu_count(),
                 maxtasksperchild=None, lock=multiprocessing.Semaphore(1)):
        self._lock = multiprocessing.Semaphore(1)
        self._max_tasks = maxtasksperchild
        self._workers = workers
        self._pool = []
        self._repopulate_pool()
        self._pool_monitor = threading.Thread(self._monitor_pool)
        self._pool_monitor.daemon = True
        self._pool_monitor.start()

    def _monitor_pool(self):
        """ This runs in its own thread and monitors the pool. """
        while True:
            self._maintain_pool()
            time.sleep(0.1)

    def _maintain_pool(self):
        """ If any workers have exited, start a new one in its place. """
        if self._join_exited_workers():
            self._repopulate_pool()

    def _join_exited_workers(self):
        """ Find exited workers and join them. """
        cleaned = False
        for i in reversed(range(len(self._pool))):
            worker = self._pool[i]
            if worker.exitcode is not None:
                # worker exited
                worker.join()
                cleaned = True
                del self._pool[i]
        return cleaned

    def _repopulate_pool(self):
        """ Start new workers if any have exited. """
        for i in range(self._workers - len(self._pool)):
            w = Worker(self._lock, self._max_tasks)
            self._pool.append(w)
            w.start()    


class Worker(multiprocessing.Process):

    def __init__(self, lock, max_tasks):
        multiprocesing.Process.__init__(self)
        self.lock = lock
        self.queue = Redis(..) # this is a redis based queue
        self.res_queue = Redis(...)
        self.max_tasks = max_tasks

     def run():
         runs = 0
         while self.max_tasks and runs < self.max_tasks:
             job = self.queue.get(block=True)
             job.results = process(job)
             with self.lock:
                 post_process(self.res_queue, job)
            if self.max_tasks:
                 runs += 1


def main():
    pool = WorkerPool(workers=4, maxtasksperchild=1000)
    # The program will block here since none of the workers are daemons.
    # It's not clear how/when you want to shut things down, but the Pool
    # can be enhanced to support that pretty easily.
导入多处理
导入线程
类WorkerPool(对象):
def uuu init uuuu(self,workers=multiprocessing.cpu\u count(),
maxtasksperchild=None,lock=multiprocessing.Semaphore(1)):
self.\u lock=多处理信号量(1)
self.\u max\u tasks=maxstasksparchild
自身工人=工人
self._pool=[]
自我重新填充池()
self.\u pool\u monitor=threading.Thread(self.\u monitor\u pool)
self.\u pool\u monitor.daemon=True
self.\u pool\u monitor.start()
def_监视器_池(自身):
“”“它在自己的线程中运行并监视池。”“”
尽管如此:
自维护池()
睡眠时间(0.1)
def_维护_池(自身):
“”“如果有员工已退出,请在其所在位置启动一个新员工。”“”
如果self.\u加入\u退出\u工作人员():
自我重新填充池()
定义加入退出工作人员(自我):
“”“找到退出的工作人员并加入他们。”“”
清除=错误
对于反向(范围(len(self.\u pool))中的i:
工人=自己。_池[i]
如果worker.exitcode不是None:
#工人退出
worker.join()
已清除=真
德尔塞尔夫(del self._pool[i]
返回清洗
def再填充池(自身):
“”“如果已退出,则启动新的工作线程。”“”
对于范围内的i(自工作人员-len(自池)):
w=工人(自我锁定、自我最大任务)
self.\u pool.append(w)
w、 开始()
类工作者(多处理.Process):
定义初始化(自我、锁定、最大任务):
多进程。进程。\u初始化\u(自)
self.lock=lock
self.queue=Redis(..)#这是一个基于Redis的队列
self.res_queue=Redis(…)
self.max\u tasks=max\u tasks
def run():
运行次数=0
当self.max_任务和运行
注tha