如何在通过Popen创建且绑定已有Pipe的进程中使用multiprocessing.Queue
问题根因
这个阻塞问题的核心来自两个已知的多进程交互特性:
- Linux fork上下文锁继承问题:fork操作只会复制调用fork的线程的执行上下文,其他线程不会被复制,但进程内所有锁的状态会被原样继承。如果fork发生时,
exit_poll线程正持有标准输入的全局锁执行sys.stdin.read(),那么孙子进程里的这个锁会永远处于被持有状态,任何尝试访问标准输入的操作都会直接死锁,导致进不去put_hello逻辑。 - Windows标准IO句柄继承+阻塞读冲突:Windows下创建子进程默认会继承父进程的所有可继承句柄,而无参数的
sys.stdin.read()会一直阻塞直到管道返回EOF,期间会占用进程内CRT层面的标准输入全局锁,multiprocessing spawn出来的孙子进程初始化标准IO时会请求该锁,导致阻塞。
解决方案
方案1:修改stdin轮询逻辑(通用,优先推荐)
不要用无参数的sys.stdin.read(),改为超时轮询+小批量读取的实现,避免长时间持有标准输入锁,同时也能正常检测父进程管道关闭的事件:
# 跨平台兼容的stdin关闭检测实现 import sys import time def exit_on_stdin_close(): try: if sys.platform == 'win32': import msvcrt while True: if msvcrt.kbhit(): if not sys.stdin.read(1): break time.sleep(0.1) else: import select while True: rlist, _, _ = select.select([sys.stdin], [], [], 0.1) if rlist: if not sys.stdin.read(1): break except: pass
方案2:创建孙子进程时显式隔离标准输入
在创建multiprocessing.Process时,手动将标准输入重定向到空设备,同时关闭无关句柄的继承,避免孙子进程触碰父进程的stdin管道:
# 修改mproc.py中创建孙子进程的代码 import subprocess p = ctx.Process( target=put_hello, args=(q,), stdin=subprocess.DEVNULL, close_fds=True )
两种方案任选其一即可复现预期输出,同时保留exit-poll线程的父进程退出检测能力,两者同时使用兼容性更好。
内容的提问来源于stack exchange,提问作者hineroptera
相关产品推荐
相关产品推荐

