【问题标题】:How to get the amount of "work" left to be done by a Python multiprocessing Pool?如何获得 Python 多处理池要完成的“工作”量?
【发布时间】:2012-12-03 19:05:11
【问题描述】:

到目前为止,每当我需要使用 multiprocessing 时,我都会手动创建一个“进程池”并与所有子进程共享一个工作队列。

例如:

from multiprocessing import Process, Queue


class MyClass:

    def __init__(self, num_processes):
        self._log         = logging.getLogger()
        self.process_list = []
        self.work_queue   = Queue()
        for i in range(num_processes):
            p_name = 'CPU_%02d' % (i+1)
            self._log.info('Initializing process %s', p_name)
            p = Process(target = do_stuff,
                        args   = (self.work_queue, 'arg1'),
                        name   = p_name)

这样我可以将东西添加到队列中,这些东西将被子进程消耗。然后我可以通过检查Queue.qsize() 来监控处理的进度:

    while True:
        qsize = self.work_queue.qsize()
        if qsize == 0:
            self._log.info('Processing finished')
            break
        else:
            self._log.info('%d simulations still need to be calculated', qsize)

现在我认为multiprocessing.Pool 可以大大简化这段代码。

我不知道如何监控仍有待完成的“工作”量。

举个例子:

from multiprocessing import Pool


class MyClass:

    def __init__(self, num_processes):
        self.process_pool = Pool(num_processes)
        # ...
        result_list = []
        for i in range(1000):            
            result = self.process_pool.apply_async(do_stuff, ('arg1',))
            result_list.append(result)
        # ---> here: how do I monitor the Pool's processing progress?
        # ...?

有什么想法吗?

【问题讨论】:

    标签: python process parallel-processing multiprocessing pool


    【解决方案1】:

    使用Manager 队列。这是一个在工作进程之间共享的队列。如果您使用普通队列,它将被每个工作人员腌制和取消腌制并因此被复制,因此每个工作人员都无法更新队列。

    然后,您可以让您的工作人员将内容添加到队列中,并在工作人员工作时监控队列的状态。您需要使用map_async 来执行此操作,因为这可以让您看到整个结果何时准备就绪,从而可以中断监控循环。

    例子:

    import time
    from multiprocessing import Pool, Manager
    
    
    def play_function(args):
        """Mock function, that takes a single argument consisting
        of (input, queue). Alternately, you could use another function
        as a wrapper.
        """
        i, q = args
        time.sleep(0.1)  # mock work
        q.put(i)
        return i
    
    p = Pool()
    m = Manager()
    q = m.Queue()
    
    inputs = range(20)
    args = [(i, q) for i in inputs]
    result = p.map_async(play_function, args)
    
    # monitor loop
    while True:
        if result.ready():
            break
        else:
            size = q.qsize()
            print(size)
            time.sleep(0.1)
    
    outputs = result.get()
    

    【讨论】:

      【解决方案2】:

      我为 async_call 提出了以下解决方案。

      简单的玩具脚本示例,但我认为应该广泛应用。

      基本上在无限循环中轮询列表生成器中结果对象的就绪值并求和以计算剩余多少已调度的池任务。

      一旦没有剩余的 break 和 join() & close()。

      根据需要在循环中添加睡眠。

      与上述解决方案的原理相同,但没有队列。如果您还跟踪最初发送池的任务数量,您可以计算完成百分比等...

      import multiprocessing
      import os
      import time
      from random import randrange
      
      
      def worker():
          print os.getpid()
      
          #simulate work
          time.sleep(randrange(5))
      
      if __name__ == '__main__':
      
          pool = multiprocessing.Pool(processes=8)
          result_objs = []
      
          print "Begin dispatching work"
      
          task_count = 10
          for x in range(task_count):
              result_objs.append(pool.apply_async(func=worker))
      
          print "Done dispatching work"
      
          while True:
              incomplete_count = sum(1 for x in result_objs if not x.ready())
      
              if incomplete_count == 0:
                  print "All done"
                  break
      
              print str(incomplete_count) + " Tasks Remaining"
              print str(float(task_count - incomplete_count) / task_count * 100) + "% Complete"
              time.sleep(.25)
      
          pool.close()
          pool.join()
      

      【讨论】:

        【解决方案3】:

        我遇到了同样的问题,并为 MapResult 对象想出了一个稍微简单的解决方案(尽管使用内部 MapResult 数据)

        pool = Pool(POOL_SIZE)
        
        result = pool.map_async(get_stuff, todo)
        while not result.ready():
            remaining = result._number_left * result._chunksize
            sys.stderr.write('\r\033[2KRemaining: %d' % remaining)
            sys.stderr.flush()
            sleep(.1)
        
        print >> sys.stderr, '\r\033[2KRemaining: 0'
        

        请注意,剩余值并不总是准确的,因为块大小通常会根据要处理的项目数四舍五入。

        您可以使用 pool.map_async(get_stuff, todo, chunksize=1) 绕过此问题

        【讨论】:

          【解决方案4】:

          从文档中,在我看来,您想要做的是以列表或其他顺序收集您的 results,然后迭代结果列表检查 ready 以构建您的输出列表。然后,您可以通过将未处于就绪状态的剩余结果对象的数量与已调度的作业总数进行比较来计算处理状态。见http://docs.python.org/2/library/multiprocessing.html#multiprocessing.pool.AsyncResult

          【讨论】:

            猜你喜欢
            • 2015-07-30
            • 1970-01-01
            • 2014-03-27
            • 2019-02-25
            • 2020-03-12
            • 2021-09-05
            • 2015-08-17
            • 2014-05-31
            • 2011-09-09
            相关资源
            最近更新 更多