Asyncio:如何从子进程读取stdout?
我遇到了一个比较简单的问题——我无法和进程的标准输出(stdout)进行通信。这个进程是一个简单的计时器,所以我想要能够启动它、停止它,并获取当前的时间。
计时器的代码是:
import argparse
import time
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument('start', type=int, default=0)
start = parser.parse_args().start
while True:
print(start)
start += 1
time.sleep(1)
if __name__ == "__main__":
main()
它的管理器代码是:
import asyncio
class RobotManager:
def __init__(self):
self.cmd = ["python", "stopwatch.py", "10"]
self.robot = None
async def start(self):
self.robot = await asyncio.create_subprocess_exec(
*self.cmd,
stdout=asyncio.subprocess.PIPE,
)
async def stop(self):
if self.robot:
self.robot.kill()
stdout = await self.robot.stdout.readline()
print(stdout)
await self.robot.wait()
self.robot = None
async def main():
robot = RobotManager()
await robot.start()
await asyncio.sleep(3)
await robot.stop()
await robot.start()
await asyncio.sleep(3)
await robot.stop()
asyncio.run(main())
但是每次调用 stdout.readline
时,返回的都是一个空的字节串。
当我把 stdout = await self.robot.stdout.readline()
改成 stdout, _ = await self.robot.communicate()
时,结果仍然是一个空的字节串。
如果在 RobotManager.start
方法的末尾加上 await self.robot.stdout.readline()
,程序就会一直卡在那里。
不过,当我去掉 stdout=asyncio.subprocess.PIPE
和所有的 readline
调用时,子进程就会像预期那样输出到终端。
我该如何正确地从子进程的标准输出中读取数据呢?
2 个回答
0
如果你不需要实时处理输出,而是可以等到程序结束后再处理,那就可以使用这个:
await proc.wait()
output = await proc.stdout.read()
在我看来,你其实只需要用proc.communicate来把输入发送给程序就可以了。
2
在这种情况下,"proc.communicate" 是不适用的,因为提问者想要中断一个正在运行的进程。Python文档中的示例代码也展示了如何直接读取管道中的标准输出,所以这样做原则上是没有问题的。
主要的问题是,计时器进程在缓存输出。你可以试着使用这个命令:["python", "-u", "stopwatch.py", "3"]
。为了调试,你还可以添加一些打印信息,显示机器人什么时候开始和结束。以下代码对我有效:
class RobotManager:
def __init__(self):
self.cmd = ["python", "-u", "stopwatch.py", "3"]
self.robot = None
async def start(self):
print("======Starting======")
self.robot = await asyncio.create_subprocess_exec(
*self.cmd,
stdout=asyncio.subprocess.PIPE,
)
async def stop(self):
if self.robot:
self.robot.kill()
stdout = await self.robot.stdout.readline()
while stdout:
print(stdout)
stdout = await self.robot.stdout.readline()
await self.robot.wait()
print("======Terminated======")
self.robot = None