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