You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python3 multiprocessing Pool类join()方法无限挂起问题排查

搞定你的无限运行问题!

核心原因:共享字典没初始化键

你的程序一直卡着不动,根本问题出在共享字典total_times没预先设置好需要的键。当子进程执行times[animal] += 1时,因为cats/dogs/bats这些键一开始不存在,会触发KeyError异常。但你用apply_async提交任务后没去获取结果,这些异常就被藏起来了,导致子进程没法正常结束,最后pool.join()就一直等着,无限卡住。

至于MacBook Silicon的架构,其实不是直接原因,但后面也会提些注意点。


一步步修复

  1. 先给共享字典初始化键
    创建manager.dict()的时候,直接把所有动物的初始计数设为0:

    total_times = manager.dict({"cats":0, "dogs":0, "bats":0})
    

    这一步是关键,不然子进程一运行就会报错卡住。

  2. (可选)及时捕获子进程异常
    用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()
    
  3. 优化多进程的随机数
    多进程里用默认的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:18:58