Python 从异步子进程读取流输出

Python 从异步子进程读取流输出,python,subprocess,python-asyncio,Python,Subprocess,Python Asyncio,我试图从子进程中运行的程序读取URL,然后计划一个异步HTTP请求,但看起来请求是同步运行的。这是因为子流程和请求都运行在同一个协程函数中吗 test.py import random import time URLS = ['http://example.com', 'http://example.com/sleep5s'] def main(): for url in random.choices(URLS, weights=(1, 1), k=5): print

我试图从子进程中运行的程序读取URL,然后计划一个异步HTTP请求,但看起来请求是同步运行的。这是因为子流程和请求都运行在同一个协程函数中吗

test.py

import random
import time

URLS = ['http://example.com', 'http://example.com/sleep5s']

def main():
    for url in random.choices(URLS, weights=(1, 1), k=5):
        print(url)
        time.sleep(random.uniform(0.5, 1))


if __name__ == '__main__':
    main()
import asyncio
import sys

import httpx

from  httpx.exceptions import TimeoutException


async def req(url):
    async with httpx.AsyncClient() as client:
        try:
            r = await client.get(url, timeout=2)
            print(f'Response {url}: {r.status_code}')
        except Exception as TimeoutException:
            print(f'TIMEOUT - {url}')
        except Exception as exc:
            print(f'ERROR - {url}')


async def run():
    proc = await asyncio.create_subprocess_exec(
        sys.executable,
        '-u',
        'test.py',
        stdout=asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.PIPE,
    )

    while True:
        line = await proc.stdout.readline()
        if not line:
            break

        url = line.decode().rstrip()
        print(f'Found URL: {url}')

        resp = await req(url)

    await proc.wait()


async def main():
    await run()


if __name__ == '__main__':
    asyncio.run(main())
main.py

import random
import time

URLS = ['http://example.com', 'http://example.com/sleep5s']

def main():
    for url in random.choices(URLS, weights=(1, 1), k=5):
        print(url)
        time.sleep(random.uniform(0.5, 1))


if __name__ == '__main__':
    main()
import asyncio
import sys

import httpx

from  httpx.exceptions import TimeoutException


async def req(url):
    async with httpx.AsyncClient() as client:
        try:
            r = await client.get(url, timeout=2)
            print(f'Response {url}: {r.status_code}')
        except Exception as TimeoutException:
            print(f'TIMEOUT - {url}')
        except Exception as exc:
            print(f'ERROR - {url}')


async def run():
    proc = await asyncio.create_subprocess_exec(
        sys.executable,
        '-u',
        'test.py',
        stdout=asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.PIPE,
    )

    while True:
        line = await proc.stdout.readline()
        if not line:
            break

        url = line.decode().rstrip()
        print(f'Found URL: {url}')

        resp = await req(url)

    await proc.wait()


async def main():
    await run()


if __name__ == '__main__':
    asyncio.run(main())
测试

$ python main.py
Found URL: http://example.com
Response http://example.com: 200
Found URL: http://example.com/sleep5s
TIMEOUT - http://example.com/sleep5s
Found URL: http://example.com/sleep5s
TIMEOUT - http://example.com/sleep5s
Found URL: http://example.com
Response http://example.com: 200
Found URL: http://example.com/sleep5s
TIMEOUT - http://example.com/sleep5s
看起来请求是同步运行的。这是因为子流程和请求都运行在同一个协程函数中吗

你的诊断是正确的<代码>等待的意思是它在tin上所说的:在得到结果之前,协同程序不会继续进行。幸运的是,asyncio使在后台运行协同程序变得容易:

tasks=[]
尽管如此:
line=wait proc.stdout.readline()
如果不是直线:
打破
url=line.decode().rstrip()
打印(f'找到的URL:{URL}')
tasks.append(异步IO.create_任务(请求(url)))
resps=asyncio.gather(*任务)
等待程序等待()
请注意:

  • asyncio.create_task()
    确保即使在我们仍在读取行时,请求也会开始被处理
  • asyncio.gather()
    确保在协同程序完成之前实际上等待所有任务。它还提供对响应的访问并传播异常(如果有)