显示Python多处理池imap\U无序调用的进度?

显示Python多处理池imap\U无序调用的进度?,python,multiprocessing,Python,Multiprocessing,我有一个脚本,它通过imap\u unordered()调用成功地执行了一组多处理池任务: p = multiprocessing.Pool() rs = p.imap_unordered(do_work, xrange(num_tasks)) p.close() # No more work p.join() # Wait for completion 但是,我的num_tasks大约是250000,因此join()会将主线程锁定10秒左右,我希望能够以增量方式回显到命令行,以显示主进程未锁

我有一个脚本,它通过
imap\u unordered()
调用成功地执行了一组多处理池任务:

p = multiprocessing.Pool()
rs = p.imap_unordered(do_work, xrange(num_tasks))
p.close() # No more work
p.join() # Wait for completion
但是,我的
num_tasks
大约是250000,因此
join()
会将主线程锁定10秒左右,我希望能够以增量方式回显到命令行,以显示主进程未锁定。比如:

p = multiprocessing.Pool()
rs = p.imap_unordered(do_work, xrange(num_tasks))
p.close() # No more work
while (True):
  remaining = rs.tasks_remaining() # How many of the map call haven't been done yet?
  if (remaining == 0): break # Jump out of while loop
  print("Waiting for", remaining, "tasks to complete...")
  time.sleep(2)

结果对象或池本身是否有指示剩余任务数的方法?我尝试使用
multiprocessing.Value
对象作为计数器(
do_work
在完成任务后调用
counter.Value+=1
操作),但是,在停止递增之前,计数器只能达到总值的约85%。

通过进一步挖掘,我自己找到了一个答案:查看
imap\u无序
结果对象的
\u目录
,我发现它有一个
\u索引
属性,随着每个任务的完成而递增。因此,这适用于日志记录,包装在
while
循环中:

p = multiprocessing.Pool()
rs = p.imap_unordered(do_work, xrange(num_tasks))
p.close() # No more work
while (True):
  completed = rs._index
  if (completed == num_tasks): break
  print "Waiting for", num_tasks-completed, "tasks to complete..."
  time.sleep(2)
但是,我确实发现,将替换为会导致更快的执行,尽管结果对象有点不同。相反,来自
map\u async
的结果对象具有
\u number\u left
属性和
ready()
方法:

p = multiprocessing.Pool()
rs = p.map_async(do_work, xrange(num_tasks))
p.close() # No more work
while (True):
  if (rs.ready()): break
  remaining = rs._number_left
  print "Waiting for", remaining, "tasks to complete..."
  time.sleep(0.5)

无需访问结果集的私有属性:

from __future__ import division
import sys

for i, _ in enumerate(p.imap_unordered(do_work, xrange(num_tasks)), 1):
    sys.stderr.write('\rdone {0:%}'.format(i/num_tasks))

我知道这是一个相当古老的问题,但当我想用python跟踪一组任务的进度时,我正在做的就是这样

from progressbar import ProgressBar, SimpleProgress
import multiprocessing as mp
from time import sleep

def my_function(letter):
    sleep(2)
    return letter+letter

dummy_args = ["A", "B", "C", "D"]
pool = mp.Pool(processes=2)

results = []

pbar = ProgressBar(widgets=[SimpleProgress()], maxval=len(dummy_args)).start()

r = [pool.apply_async(my_function, (x,), callback=results.append) for x in dummy_args]

while len(results) != len(dummy_args):
    pbar.update(len(results))
    sleep(0.5)
pbar.finish()

print results
基本上,您将apply_async与callbak一起使用(在本例中,它是将返回的值附加到列表中),因此您不必等待执行其他操作。然后,在一个while循环内,检查工作的进度。在本例中,我添加了一个小部件以使其看起来更好

输出:

4 of 4                                                                         
['AA', 'BB', 'CC', 'DD']

希望有帮助。

我创建了一个自定义类来创建进度打印输出。也许这有助于:

from multiprocessing import Pool, cpu_count


class ParallelSim(object):
    def __init__(self, processes=cpu_count()):
        self.pool = Pool(processes=processes)
        self.total_processes = 0
        self.completed_processes = 0
        self.results = []

    def add(self, func, args):
        self.pool.apply_async(func=func, args=args, callback=self.complete)
        self.total_processes += 1

    def complete(self, result):
        self.results.extend(result)
        self.completed_processes += 1
        print('Progress: {:.2f}%'.format((self.completed_processes/self.total_processes)*100))

    def run(self):
        self.pool.close()
        self.pool.join()

    def get_results(self):
        return self.results
我个人的最爱--在并行运行和提交时为您提供一个漂亮的小进度条和完成ETA

from multiprocessing import Pool
import tqdm

pool = Pool(processes=8)
for _ in tqdm.tqdm(pool.imap_unordered(do_work, tasks), total=len(tasks)):
    pass

当我试图检查它的进度时,我发现工作已经完成了。这就是我使用的

pip安装tqdm

from multiprocessing import Pool
from tqdm import tqdm

tasks = range(5)
pool = Pool()
pbar = tqdm(total=len(tasks))

def do_work(x):
    # do something with x
    pbar.update(1)

pool.imap_unordered(do_work, tasks)
pool.close()
pool.join()
pbar.close()

这应该适用于所有类型的多处理,无论它们是否阻塞。

尝试这种简单的基于队列的方法,它也可以用于池。请注意,启动进度条后打印任何内容都会导致进度条移动,至少对于此特定进度条是这样。(PyPI的进度1.5)

导入时间
从progress.bar导入栏
def状态栏(队列统计,n组,n):
条形=条形('进度',最大值=n)
完成=0
完成时
根据Tim的建议,您可以使用
TQM
imap
来解决此问题。我刚刚偶然发现了这个问题,并调整了
imap\u unordered
解决方案,以便访问映射结果。下面是它的工作原理:

来自多处理导入池的

导入TQM
池=多处理。池(进程=4)
映射的_值=列表(tqdm.tqdm(pool.imap_无序(do_work,range(num_tasks)),total=len(值)))

如果您不关心作业返回的值,则不需要将列表分配给任何变量。

一个使用
池的简单解决方案。apply_async()


我在Python2.7.6和rs上测试了这一点。“剩余的块数”似乎是剩余的块数。因此,如果rs.\u chunksize不是1,那么rs.\u number\u left将不是剩余列表项的数量。我应该将此代码放在哪里?我的意思是,直到
rs
的内容被知道并且有点晚了,这才执行?@WakanTanka:它在剥离额外线程后进入主脚本。在我最初的示例中,它进入“while”循环,其中
rs
已经启动了其他线程。请编辑您的问题和/或答案以显示最小的工作示例。我没有在任何循环中看到
rs
,我是多处理新手,这会有所帮助。非常感谢。至少在
Python3.5
中,使用
\u number\u left
的解决方案不起作用<代码>\u number\u left表示仍有待处理的块。例如,如果我希望将50个元素并行传递给我的函数,那么对于具有3个进程的线程池
\u map\u async()
将创建10个块,每个块包含5个元素<代码>\u number\u left则表示这些块中有多少块已经完成。我只在代码退出(不是每次迭代)后看到打印输出。你有什么建议吗?@Hanansteingart:它在我的系统(Ubuntu)上可以很好地使用Python2和Python3。我使用了
def do_word(*a):time.sleep(.1)
作为示例。如果不行的话
import time
from progress.bar import Bar

def status_bar( queue_stat, n_groups, n ):

    bar = Bar('progress', max = n)  

    finished = 0
    while finished < n_groups:

        while queue_stat.empty():
            time.sleep(0.01)

        gotten = queue_stat.get()
        if gotten == 'finished':
            finished += 1
        else:
            bar.next()
    bar.finish()


def process_data( queue_data, queue_stat, group):

    for i in group:

        ... do stuff resulting in new_data

        queue_stat.put(1)

    queue_stat.put('finished')  
    queue_data.put(new_data)

def multiprocess():

    new_data = []

    groups = [[1,2,3],[4,5,6],[7,8,9]]
    combined = sum(groups,[])

    queue_data = multiprocessing.Queue()
    queue_stat = multiprocessing.Queue()

    for i, group in enumerate(groups): 

        if i == 0:

            p = multiprocessing.Process(target = status_bar,
                args=(queue_stat,len(groups),len(combined)))
                processes.append(p)
                p.start()

        p = multiprocessing.Process(target = process_data,
        args=(queue_data, queue_stat, group))
        processes.append(p)
        p.start()

    for i in range(len(groups)):
        data = queue_data.get() 
        new_data += data

    for p in processes:
        p.join()
from multiprocessing import Pool
from tqdm import tqdm
from time import sleep


def work(x):
    sleep(0.2)
    return x**2


n = 10

with Pool(4) as p, tqdm(total=n) as pbar:
    res = [p.apply_async(
        work, args=(i,), callback=lambda _: pbar.update(1)) for i in range(n)]
    results = [p.get() for p in res]