如何在不阻塞的情况下发出请求(使用 asyncio)?

How to make request without blocking (using asyncio)?

我想使用 asyncio 实现以下目标:

# Each iteration of this loop MUST last only 1 second
while True:
    # Make an async request

    sleep(1)

但是,我看到的唯一示例使用了

的一些变体
async def my_func():
    loop = asyncio.get_event_loop()
    await loop.run_in_executor(None, requests.get, 'http://www.google.com')

loop = asyncio.get_event_loop()
loop.run_until_complete(my_func())

但是run_until_complete阻塞了!在我的 while 循环的每次迭代中使用 run_until_complete 会导致循环阻塞。

我花了最后几个小时试图弄清楚如何正确地 运行 一个非阻塞任务(用 async def 定义)但没有成功。我一定遗漏了一些明显的东西,因为像这样简单的东西肯定很简单。我怎样才能实现我所描述的?

run_until_complete 运行主事件循环。它不是 "blocking" 可以这么说,它只是运行事件循环,直到你作为参数传递的协程 returns。它必须挂起,否则程序将停止或被下一条指令阻塞。

很难说出你想要实现什么,但这一段代码确实做了一些事情:

async def my_func():
    loop = asyncio.get_event_loop()
    while True:
        res = await loop.run_in_executor(None, requests.get, 'http://www.google.com')
        print(res)
        await asyncio.sleep(1)
loop = asyncio.get_event_loop()
loop.run_until_complete(my_func())

它会每秒在Google 主页上执行一次GET 请求,弹出一个新线程来执行每个请求。您可以通过 运行 几乎并行的多个请求说服自己它实际上是非阻塞的:

async def entrypoint():
    await asyncio.wait([
        get('https://www.google.com'),
        get('https://www.whosebug.com'),
    ])

async def get(url):
    loop = asyncio.get_event_loop()
    while True:
        res = await loop.run_in_executor(None, requests.get, url)
        print(url, res)
        await asyncio.sleep(1)

loop = asyncio.get_event_loop()
loop.run_until_complete(entrypoint())

另一件需要注意的事情是,您每次都在单独的线程中 运行 请求。它有效,但有点像 hack。您应该使用真正的异步 HTTP 客户端,例如 aiohttp.

这是Python3.10

asyncio 是单线程执行,使用 await 将 cpu 交给其他函数,直到等待完成。

    import asyncio
    async def my_func(t):
        print("Start my_func")
        await asyncio.sleep(t)  # The await yields cpu, while we wait
        print("Exit my_func")

    async def main():
        asyncio.ensure_future(my_func(10)) # Schedules on event loop, we might want to save the returned future to later check for completion.
        print("Start main")
        await asyncio.sleep(1) # The await yields cpu, giving my_func chance to start.
        print("running other stuff")
        await asyncio.sleep(15)
        print("Exit main")

    if __name__ == "__main__":
        asyncio.run(main())  # Starts event loop