Asyncio如何正确读取子进程的标准输出?
Asyncio如何正确读取子进程的标准输出?
我来帮你梳理下问题的根源,以及对应的解决办法:
问题出在哪?
你的stopwatch.py里用了print(start),但Python的输出缓冲策略会根据stdout的类型变化:
- 当stdout是终端时,默认是行缓冲,print后会立刻把内容输出到屏幕;
- 但当我们把stdout重定向到管道(也就是设置
stdout=asyncio.subprocess.PIPE)时,Python会切换成块缓冲——意思是输出会先攒在缓冲区里,攒够一定大小才会发送到管道。
这就导致你的子进程虽然每秒都在print,但内容一直留在缓冲区里,没传到父进程那边:
- 你调用
readline()时会因为没有可读取的内容挂起; - 杀掉进程后再读,缓冲区里的内容可能还没来得及刷新,所以读到空字符串。
解决办法(三种任选其一)
1. 修改子进程代码,强制刷新输出
在stopwatch.py的print语句里加上flush=True,让每次print都立刻把内容刷到管道:
print(start, flush=True)
2. 启动子进程时禁用Python缓冲(不用改子进程代码)
通过设置环境变量PYTHONUNBUFFERED=1,强制Python不使用输出缓冲:
import os # 记得导入os async def start(self): env = os.environ.copy() env['PYTHONUNBUFFERED'] = '1' self.robot = await asyncio.create_subprocess_exec( *self.cmd, stdout=asyncio.subprocess.PIPE, env=env )
3. 用-u参数启动Python解释器(不用改子进程代码)
直接在启动命令里加上-u参数,让Python进入无缓冲模式:
self.cmd = ["python", "-u", "stopwatch.py", "10"]
额外优化:读取所有输出
原代码里readline()只能读一行,如果你想获取子进程运行期间的所有输出,可以在stop方法里循环读取直到stdout关闭:
async def stop(self): if self.robot: self.robot.kill() # 循环读取所有剩余输出 while True: line = await self.robot.stdout.readline() if not line: break # 转成字符串并去掉换行符 print(f"子进程输出:{line.decode().strip()}") await self.robot.wait() self.robot = None
备注:内容来源于stack exchange,提问作者Black Jack
相关产品推荐
相关产品推荐

