为何fork()前父进程向multiprocessing.Queue存数据会阻塞?
问题分析:os.fork()配合multiprocessing.Queue导致阻塞的原因及解决办法
问题复现
你的代码如下:
import os, time from multiprocessing import Queue q = Queue(1000) q.put(1) # Line 6 result = os.fork() if result == 0: while True: q.put(2) time.sleep(1) elif result > 0: while True: print(q.get()) else: raise Exception('Fork failed: %d' % result)
预期输出是1 2 2 2 ...,但实际只输出1后就陷入阻塞;移除第6行的q.put(1)操作,程序则能正常输出2 2 2 ...。
原因解析
这既不是死锁,也不是multiprocessing.Queue的bug,而是错误地将底层os.fork()与高层multiprocessing.Queue混用导致的问题。
multiprocessing.Queue的实现依赖父进程中启动的后台线程(比如负责将队列缓冲区数据同步到跨进程管道的线程)。当调用os.fork()时,子进程会复制父进程的内存空间,但不会继承父进程的线程——子进程里Queue对应的后台线程根本不会运行。
具体到你的场景:
- 第6行执行
q.put(1)后,父进程已经启动了Queue的后台线程。 - 调用
os.fork()创建子进程,子进程复制了Queue的内存状态,但没有复制后台线程。 - 子进程调用
q.put(2)时,数据只能存入子进程本地的缓冲区,却没有后台线程把数据传到父进程能读取的管道中。 - 父进程取完初始的
1后,就一直等待新数据,最终陷入阻塞。
而如果去掉第6行,fork时Queue还未被使用,后台线程尚未启动,子进程调用q.put(2)时会尝试启动自己的后台线程,但这种情况属于未定义行为,只是刚好在你的环境中能运行,并不推荐依赖。
解决方案
方案1:使用multiprocessing.Process代替os.fork()(推荐)
multiprocessing模块的Process类是专门设计用来配合Queue等组件使用的,它会在创建子进程时自动处理Queue的内部初始化,确保父子进程的Queue能正常通信。修改后的代码如下:
import time from multiprocessing import Queue, Process def child_task(q): while True: q.put(2) time.sleep(1) if __name__ == "__main__": q = Queue(1000) q.put(1) # 创建子进程并启动 child_proc = Process(target=child_task, args=(q,)) child_proc.start() # 父进程持续读取队列 while True: print(q.get())
方案2:fork后再创建Queue(仅适用于不需要共享队列的场景)
如果必须使用os.fork(),那么要在fork完成后,分别在父进程和子进程中创建独立的Queue实例——但这种方式无法实现父子进程共享队列数据,仅适用于不需要跨进程传递数据的场景。
内容的提问来源于stack exchange,提问作者Eric Stdlib
相关产品推荐
相关产品推荐

