Python 是否可以同时读取多个异步IO流?

Python 是否可以同时读取多个异步IO流?,python,select,stream,python-asyncio,Python,Select,Stream,Python Asyncio,我需要读取几个并发运行的异步IO任务的输出 这些任务实际上是使用asyncio.create_subprocess_exec()创建的 以最简单的形式,我需要打印单个进程的stdout/stderr,同时在单独的字符串中累加行 我当前的(工作)代码是: 这段代码的问题是在进程终止之前我什么也看不到;一些派生的进程可能需要几分钟才能完成,并且会在执行时打印“有趣的”信息。如何在捕获时立即打印(或记录)输出?(我知道忽略底层流程的捕获会打印出来,但我也需要捕获) 我试着做一些类似的事情: _stdo

我需要读取几个并发运行的异步IO任务的输出

这些任务实际上是使用asyncio.create_subprocess_exec()创建的

以最简单的形式,我需要打印单个进程的stdout/stderr,同时在单独的字符串中累加行

我当前的(工作)代码是:

这段代码的问题是在进程终止之前我什么也看不到;一些派生的进程可能需要几分钟才能完成,并且会在执行时打印“有趣的”信息。如何在捕获时立即打印(或记录)输出?(我知道忽略底层流程的捕获会打印出来,但我也需要捕获)

我试着做一些类似的事情:

_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()