Python 是否可以同时读取多个异步IO流?
我需要读取几个并发运行的异步IO任务的输出 这些任务实际上是使用asyncio.create_subprocess_exec()创建的 以最简单的形式,我需要打印单个进程的stdout/stderr,同时在单独的字符串中累加行 我当前的(工作)代码是: 这段代码的问题是在进程终止之前我什么也看不到;一些派生的进程可能需要几分钟才能完成,并且会在执行时打印“有趣的”信息。如何在捕获时立即打印(或记录)输出?(我知道忽略底层流程的捕获会打印出来,但我也需要捕获) 我试着做一些类似的事情:Python 是否可以同时读取多个异步IO流?,python,select,stream,python-asyncio,Python,Select,Stream,Python Asyncio,我需要读取几个并发运行的异步IO任务的输出 这些任务实际上是使用asyncio.create_subprocess_exec()创建的 以最简单的形式,我需要打印单个进程的stdout/stderr,同时在单独的字符串中累加行 我当前的(工作)代码是: 这段代码的问题是在进程终止之前我什么也看不到;一些派生的进程可能需要几分钟才能完成,并且会在执行时打印“有趣的”信息。如何在捕获时立即打印(或记录)输出?(我知道忽略底层流程的捕获会打印出来,但我也需要捕获) 我试着做一些类似的事情: _stdo
_stdout = ''
while True:
data = process.stdout.readline()
if not data:
break
print(data)
_stdout += data.decode()
但我不知道如何将其扩展到多个流(在本例中,只是stdout/stderr,但可能扩展到多个程序)。是否有类似于select()调用的内容
有什么提示吗欢迎
是否有类似于select()
call的功能
答案必须是肯定的,因为asyncio完全是围绕调用select()
构建的。然而,如何将其转换为流级别的select并不总是显而易见的。需要注意的是,您不应该尝试精确地选择流——相反,应该开始阅读流并依赖于选择协同路由进度的能力。因此,与select()
等效的方法是使用asyncio.wait(return\u when=FIRST\u COMPLETED)
在循环中驱动读取
一个更为优雅的替代方案是产生单独的任务,每个任务都做自己的事情,然后让它们并行运行。代码比使用select
更容易理解,可以归结为对的单个调用,但实际上,asyncio执行的正是所请求的select()
:
import asyncio, sys, io
async def _read_all(stream, echo):
# helper function to read the whole stream, optionally
# displaying data as it arrives
buf = io.BytesIO() # BytesIO is preferred to +=
while True:
chunk = await stream.read(4096)
if len(chunk) == 0:
break
buf.write(chunk)
if echo:
sys.stdout.buffer.write(chunk)
return buf.getvalue()
async def run_command(*args, stdin=None, echo=False):
process = await asyncio.create_subprocess_exec(
*args,
stdin=asyncio.subprocess.PIPE,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
)
if stdin is not None:
process.stdin.write(stdin)
process.stdin.close()
stdout, stderr = await asyncio.gather(
_read_all(process.stdout, echo),
_read_all(process.stderr, echo)
)
return process.returncode, stdout.decode().strip(), stderr.decode().strip()
请注意,asyncio的write()
不是一个协同程序,它默认在后台写入,因此我们不需要将写入包含在我们gather()
的协同程序中
import asyncio, sys, io
async def _read_all(stream, echo):
# helper function to read the whole stream, optionally
# displaying data as it arrives
buf = io.BytesIO() # BytesIO is preferred to +=
while True:
chunk = await stream.read(4096)
if len(chunk) == 0:
break
buf.write(chunk)
if echo:
sys.stdout.buffer.write(chunk)
return buf.getvalue()
async def run_command(*args, stdin=None, echo=False):
process = await asyncio.create_subprocess_exec(
*args,
stdin=asyncio.subprocess.PIPE,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
)
if stdin is not None:
process.stdin.write(stdin)
process.stdin.close()
stdout, stderr = await asyncio.gather(
_read_all(process.stdout, echo),
_read_all(process.stderr, echo)
)
return process.returncode, stdout.decode().strip(), stderr.decode().strip()