如何获得Python多处理池剩余的“工作量”?

时间:2012-12-03 19:05:12

标签: python process parallel-processing multiprocessing pool

到目前为止,每当我需要使用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?
        # ...?

有什么想法吗?

4 个答案:

答案 0 :(得分:14)

使用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()

答案 1 :(得分:2)

我遇到了同样的问题,并为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)

进行循环播放

答案 2 :(得分:1)

我想出了async_call的解决方案。

琐碎的玩具脚本示例但我应该广泛应用。

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

一旦没有剩下休息和加入()&关闭()。

根据需要在循环中添加sleep。

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

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 :(得分:0)

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