使用subprocess时sys.stdin.read()阻塞问题及通信故障原因排查
问题原因与解决方案
核心原因:sys.stdin.read() 等待EOF信号导致阻塞
sys.stdin.read() 这个方法的行为是持续读取输入流中的所有数据,直到输入流被关闭(收到EOF文件结束符)才会返回。如果输入流一直处于打开状态且没有新数据写入,程序就会一直卡在这行代码,陷入阻塞。
放到你的代码流程里,具体阻塞逻辑是这样的:
script1.py输出 "Hello world" 并刷新stdout后,立刻执行answer = str(sys.stdin.read()),开始等待从stdin获取输入;test.py读取到script1的输出后,调用proc2处理数据,再把proc2的结果写入proc1的stdin,但只做了proc1.stdin.write(answer),既没有调用flush()确保数据真正写入管道,也没有调用close()向script1的stdin发送EOF信号;script1因为一直没收到EOF,会永远卡在sys.stdin.read()这行,导致整个程序阻塞。
另外还有个潜在细节:当 script1 没结束时,它的stdout管道并未关闭,test.py 中的 proc1.stdout.read() 其实也会等待EOF,但因为你在 script1 里做了 sys.stdout.flush(),管道里的"Hello world"会被提前发送,test.py 能读到这部分数据,但本质上这是个不严谨的写法——正确的做法是子进程关闭stdout后,父进程的read()才会正常返回。
解决方案
方案1:手动管理管道,发送EOF信号
修改 test.py 的最后几行,写完数据后刷新并关闭 proc1 的stdin,同时等待子进程结束:
proc1.stdin.write(answer) proc1.stdin.flush() # 确保数据写入管道 proc1.stdin.close() # 发送EOF信号给script1 proc1.wait() # 等待script1执行完毕,避免僵尸进程
方案2:改用行读取,约定换行符为结束标记
如果不需要读取整个输入流,而是按行读取,可以把 script1.py 中的 sys.stdin.read() 改成 sys.stdin.readline(),这样只要收到换行符就会返回,不需要等EOF:
# script1.py 修改后 import sys sys.stdout.write("Hello world") sys.stdout.flush() answer = str(sys.stdin.readline()) # 读取一行,遇到换行就返回
同时 test.py 写入时要加上换行符:
proc1.stdin.write(answer + b'\n') proc1.stdin.flush()
方案3:使用 communicate() 简化管道管理
subprocess.Popen 的 communicate() 方法会自动处理输入写入、输出读取、管道关闭和等待子进程结束,是更稳妥的写法,推荐使用:
#!/usr/bin/python3 from subprocess import Popen, PIPE # 启动script1,获取它的输出 proc1 = Popen(["python3","script1.py"], stdin=PIPE, stdout=PIPE) first_output, _ = proc1.communicate(input=None) # 先读取script1的初始输出 print("[test.py:] " + str(first_output)) # 启动script2,传入first_output并获取返回结果 proc2 = Popen(["python3","script2.py"], stdin=PIPE, stdout=PIPE) answer, _ = proc2.communicate(input=first_output) print("[test.py:] " + str(answer)) # 把answer传给script1,等待它执行完毕 proc1.communicate(input=answer)
内容的提问来源于stack exchange,提问作者ceske
相关产品推荐
相关产品推荐

