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

Python Multiprocessing未使用time.sleep时异常崩溃问题咨询

问题原因解析

核心触发点

这个错误是macOS平台Python 3.8+版本多进程默认spawn启动模式的竞态问题,和你注释掉sleep后任务执行速度大幅提升直接相关。

具体原理

  • macOS从Python 3.8开始,多进程默认启动方式从fork改为spawn:spawn模式启动子进程时,父进程需要先把队列、信号量等同步对象的状态序列化后传给子进程,子进程反序列化这些状态、重建对应内核资源后才能正常运行。
  • 你注释掉sleep后,mul和plus函数的计算量极小,所有任务会在极短时间内执行完成:主进程从done_queue获取完所有结果、向任务队列写入4个STOP标记后,test函数就执行完毕,主进程会直接退出,操作系统会回收主进程持有的所有内核资源,包括队列底层依赖的SemLock信号量对应的文件句柄。
  • 而spawn模式启动子进程有一定开销,部分子进程此时还没完成启动流程,还在执行反序列化重建SemLock的逻辑,对应的内核资源已经被系统回收,就会抛出你看到的FileNotFoundError异常。
  • 原代码保留sleep时,每个任务执行都会有延迟,主进程会长时间阻塞在done_queue.get()调用上等待结果,不会提前退出,所有子进程都有足够时间完成启动,因此不会触发问题。

修复方案

原官方示例只是演示多进程队列的基础用法,没有加子进程等待逻辑,实际生产环境使用时,在所有任务下发完成后需要调用join等待所有子进程退出,避免主进程提前释放资源:

def test():
    NUMBER_OF_PROCESSES = 4
    TASKS1 = [(mul, (i, 7)) for i in range(20)]
    TASKS2 = [(plus, (i, 8)) for i in range(10)]

    # Create queues
    task_queue = Queue()
    done_queue = Queue()

    # Submit tasks
    for task in TASKS1:
        task_queue.put(task)

    # 存储所有子进程对象
    processes = []
    # Start worker processes
    for i in range(NUMBER_OF_PROCESSES):
        p = Process(target=worker, args=(task_queue, done_queue))
        p.start()
        processes.append(p)

    # Get and print results
    print('Unordered results:')
    for i in range(len(TASKS1)):
        print('\t', done_queue.get())

    # Add more tasks using `put()`
    for task in TASKS2:
        task_queue.put(task)

    # Get and print some more results
    for i in range(len(TASKS2)):
        print('\t', done_queue.get())

    # Tell child processes to stop
    for i in range(NUMBER_OF_PROCESSES):
        task_queue.put('STOP')
    
    # 新增:等待所有子进程正常退出后,主进程再退出
    for p in processes:
        p.join()

添加join逻辑后,哪怕完全注释掉两个函数里的sleep调用,程序也可以正常运行不会崩溃。

内容的提问来源于stack exchange,提问作者akaButters

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.17 13:54:04