使用Process的Python多处理:消耗大内存

时间:2016-08-08 04:18:56

标签: python memory-leaks python-multiprocessing

我从单个python代码运行多个进程:

代码段:

while 1:
   if sqsObject.msgCount() > 0:
        ReadyMsg = sqsObject.readM2Q()
        if ReadyMsg == 0:
            continue
        fileName = ReadyMsg['fileName']
        dirName  = ReadyMsg['dirName']
        uuid         = ReadyMsg['uid']
        guid         = ReadyMsg['guid']
        callback     = ReadyMsg['callbackurl']

        # print ("Trigger Algorithm Process")
        if(countProcess < maxProcess):

           try:
             retValue = Process(target=dosomething, args=(dirName, uuid,guid,callback))
             processArray.append(retValue)
             retValue.start()
             countProcess = countProcess + 1
           except:
             print "Cannot Run Process"
        else:
           for i in range(len(processArray)):
              if (processArray[i].is_alive() == True):
                 continue
              else:
                 try:
                    #print 'Restart Process'
                    processArray[i] = Process(target=dosomething, args=(dirName,uuid,guid,callback))
                    processArray[i].start()
                 except:
                    print "Cannot Run Process"


   else: # No more request to service

       for i in range(len(processArray)):
            if (processArray[i].is_alive() == True):
                processRunning = 1
                break
            else:
                continue

      if processRunning == 0:
           countProcess = 0

      else:
           processRunning = 0

这里我正在读取队列中的消息,并创建一个在该消息上运行算法的过程。我正在设置maxProcess的上限。因此,在达到maxProcess之后,我想通过检查is_alive()来重用不活动的processArray槽。

此过程适用于较少数量的进程,但是,对于大量消息说100,内存消耗通过屋顶。我想通过重复使用进程槽来泄漏。

不确定过程中出了什么问题。

提前感谢您发现错误或明智的建议。

2 个答案:

答案 0 :(得分:0)

  

不确定过程中出了什么问题。

即使达到maxProcess计数,您似乎也在创建与消息一样多的进程。

  

我在想通过重复使用流程插槽来泄漏。

无需自行管理流程。只需使用process pool

 # before your while loop starts
 from multiprocessing import Pool
 pool = Pool(processes=max_process)
 while 1:
   ...
   # instead of creating a new Process
   res = pool.apply_async(dosomething, 
                          args=(dirName,uuid,guid,callback)) 
 # after the while loop has finished
 # -- wait to finish
 pool.close()
 pool.join()

提交工作的方式

请注意,Pool class支持多种提交作业的方式:

  • apply_async - 一次发送一条消息
  • map_async - 一次发送一大块消息

如果邮件的到达速度足够快,可能最好收集其中几个(例如,一次10个或100个,具体取决于所做的实际处理),并使用map向其提交“小批量”目标函数:

...
while True:
    messages = []
    # build mini-batch of messages
    while len(messages) < batch_size:
        ... # get message
        messages.append((dirName,uuid,guid,callback))
    pool.map_async(dosomething, messages)

为避免dosomething留下内存泄漏,您可以要求池在消耗了一些消息后重新启动进程:

max_tasks = 5 # some sensible number
Pool(max_processes, maxtasksperchild=max_tasks)

分发

如果使用此方法仍然超出内存容量,请考虑使用分布式方法,即添加更多计算机。使用Celery非常简单,来自上面:

# tasks.py
@task
def dosomething(...):
   ... # same code as before

# driver.py
  while True:
     ... # get messages as before
     res = somefunc.apply_async(args=(dirName,uuid,guid,callback))  

答案 1 :(得分:0)

总之,你的代码很奇怪: - )

它不是an mvce,所以没有其他人可以测试它,但只是看着它,你在内循环中有这个(略微简化的)结构:

if count < limit:
    ... start a new process, and increment count ...
else:
    do things that can potentially start even more processes
    (but never, ever, decrease count)

这充其量是不明智的。

在任何地方都没有流程实例join()的调用。 (我们稍后会回到外部循环及其else的情况。)

让我们更仔细地看一下内循环else案例代码:

   for i in range(len(processArray)):
        if (processArray[i].is_alive() == True):

不考虑不必要的== True测试 - 这有点风险,因为is_alive()方法没有明确承诺返回TrueFalse,只是这工作是boolean-ly-consideration this description from the documentation(此链接转到py2k docs但是py3k是相同的,而你的print语句暗示你的代码仍然是py2k):

  

is_alive()

     

返回进程是否存活。

     

粗略地说,从start()方法返回到子进程终止之前,进程对象仍处于活动状态。

由于我们无法查看dosomething的代码,因此很难说这些内容是否会终止。可能他们(通过退出),但如果他们没有,或者不是很快,我们可能会在这里遇到问题,我们只是放弃我们从外部循环中拉出队列的消息。

如果他们终止,我们只需通过覆盖它从数组中删除进程引用:

            processArray[i] = Process(...)

丢弃processArray [i]中的先前值。目前尚不清楚你是否已将其保存在其他任何地方,但如果你没有,那么Process实例将被丢弃,现在实际上不可能调用其join()方法

某些Python数据结构倾向于在放弃时自行清理(例如,打开流刷新输出并根据需要关闭),但多进程代码似乎不会自动加入()其子进程。所以这可能是问题的根源,也可能是问题的来源。

最后,每当我们到达外部循环中的else情况时,我们对任何活动进程都有相同的奇怪搜索 - 顺便说一下,它可以更清楚地写成:

if any(p.is_alive() for p in processArray):

只要我们不关心哪些特定的是活着的,哪些不是 - 如果没有人将自己报告为活着,我们会重置计数,但绝不会做任何事情变量processArray,以便每个processArray[i]仍然保存Process实例的标识。 (所以至少我们可以在每个上面调用join,不包括因覆盖而丢失的任何内容。)

与使用Pool及其multiprocess.Poolapply方法相比,您最好不要自行构建自己的apply_asyncmiraculixx's answer。< / p>