Python3 multiprocessing Pool类join()方法无限挂起问题排查
搞定你的无限运行问题!
核心原因:共享字典没初始化键
你的程序一直卡着不动,根本问题出在共享字典total_times没预先设置好需要的键。当子进程执行times[animal] += 1时,因为cats/dogs/bats这些键一开始不存在,会触发KeyError异常。但你用apply_async提交任务后没去获取结果,这些异常就被藏起来了,导致子进程没法正常结束,最后pool.join()就一直等着,无限卡住。
至于MacBook Silicon的架构,其实不是直接原因,但后面也会提些注意点。
一步步修复
先给共享字典初始化键
创建manager.dict()的时候,直接把所有动物的初始计数设为0:total_times = manager.dict({"cats":0, "dogs":0, "bats":0})这一步是关键,不然子进程一运行就会报错卡住。
(可选)及时捕获子进程异常
用apply_async的时候,最好把每个任务存起来,最后调用get()获取结果,这样如果子进程出问题,你能马上看到异常信息,不会一脸懵:jobs = [] for i in range(processors): job = pool.apply_async(f, args=(calculations_per_processor, total_times)) jobs.append(job) # 挨个等任务完成,有异常直接抛出来 for job in jobs: job.get()优化多进程的随机数
多进程里用默认的random可能会出现多个子进程生成一样的随机序列,影响结果的随机性。可以在子进程函数开头重新初始化随机种子:def f(how_many_times, times): random.seed() # 用系统时间重新生成种子 # 后面的代码不变...
修复后的完整代码
import random import time import multiprocessing as mp import math probs = {"cats":0.10,"dogs": 0.80, "bats": 0.10} calculations = 10000 processors = 8 def f(how_many_times, times): random.seed() for i in range(how_many_times): rand_num = random.random() total = 0 for animal, prob in probs.items(): total += prob if total > rand_num: times[animal] += 1 break if __name__ == '__main__': start_time = time.time() manager = mp.Manager() # 初始化所有动物的计数为0 total_times = manager.dict({"cats":0, "dogs":0, "bats":0}) pool = mp.Pool(processors) calculations_per_processor = math.floor(calculations/processors) jobs = [] for i in range(processors): job = pool.apply_async(f, args=(calculations_per_processor, total_times)) jobs.append(job) # 等待所有任务完成,捕获可能的异常 for job in jobs: job.get() pool.close() pool.join() print("统计结果:", total_times) print("--- %s seconds ---" % (time.time() - start_time))
关于MacBook Silicon的小提示
Python在ARM架构的Mac上默认用spawn方式启动子进程(不是以前的fork),这种方式会重新加载整个模块,所以你代码里的if __name__ == '__main__'保护非常重要(你已经写了,很好)。如果之后遇到其他多进程奇怪问题,可以试试显式指定启动方式:
pool = mp.Pool(processors, mp_context=mp.get_context('spawn'))
内容的提问来源于stack exchange,提问作者Mojis
相关产品推荐
相关产品推荐

