我在解析器中关闭一个进程池时遇到了一些麻烦[可能]。完成所有任务后,它会挂起并且什么也不做,cpu使用率约为1%。
profiles_pool = multiprocessing.Pool(processes=4)
pages_pool = multiprocessing.Pool(processes=4)
m = multiprocessing.Manager()
pages = m.list(['URL'])
pages_done = m.list()
while True:
# grab all links
res = pages_pool.imap_unordered(deco_process_people, pages, chunksize=1)
pages_done += pages
pages = []
for new_users,new_pages in res:
users.update(new_users)
profile_tasks = [ (new_users[i]['link'],i) for i in new_users ]
# enqueue grabbed links for parsing
profiles_pool.map_async(deco_process_profiles,
profile_tasks, chunksize=2,
callback=profile_update_callback)
# i dont need a result of map_async actually
# callback will apply parsed data to users dict
# users dict is an instance of Manager.dict()
for p in new_pages:
if p not in pages_done and p not in pages:
pages.append(p)
# we need more than 900 pages to be parsed for bug occurrence
#if len(pages) == 0:
if len(pages_done) > 900:
break
#
# closing other pools
#
# ---- the last printed string:
print 'Closing profiles pool',
sys.stdout.flush()
profiles_pool.close()
profiles_pool.join()
print 'closed'
我猜问题是池队列中的错误打开任务计算,但我不是shure而且无法检查这个 - idk如何获取任务队列长度。
它可以是什么,在哪里先看?
答案 0 :(得分:1)
最明显的问题是pages_done
是一个同步的Manager.list对象(因此每个进程都可以原子方式访问它),但是当pages
以这样的方式启动时,它很快就变成了普通的un(多)处理列表:
pages_done += pages
pages = []
第二个引用行将pages
重新绑定到一个新的空普通列表。
即使你删除了第二行pages
的所有元素(而不是重新绑定赋值),你也可能遇到一场比赛,其中(例如)pages
有A,B和当你在第一行上做+=
但在第二行中成为A,B,C和D时,C就在其中。
快速解决方法是逐项取消pages
项,然后将它们一次放入pages_done
个(效率不高)。最好不要让这些不是共享的数据结构;在引用的代码中它看起来不像它们(我假设一些不带引号的代码依赖于它 - 因为否则pages
的重新绑定无论如何都是红鲱鱼!)。
答案 1 :(得分:1)
我发现了一个错误的原因:“如果pool.map的可迭代参数为空,则多处理池对象的连接方法会挂起”