学习异步:“从未等待协程”警告错误

Ant*_*rvy 2 python python-asyncio aiohttp

我正在尝试学习在Python中使用asyncio优化脚本。我的示例返回coroutine was never awaited警告,您可以帮助您理解并找到解决方法吗?

import time 
import datetime
import random
import asyncio

import aiohttp
import requests

def requete_bloquante(num):
    print(f'Get {num}')
    uid = requests.get("https://httpbin.org/uuid").json()['uuid']
    print(f"Res {num}: {uid}")

def faire_toutes_les_requetes():
    for x in range(10):
        requete_bloquante(x)

print("Bloquant : ")
start = datetime.datetime.now()
faire_toutes_les_requetes()
exec_time = (datetime.datetime.now() - start).seconds
print(f"Pour faire 10 requêtes, ça prend {exec_time}s\n")

async def requete_sans_bloquer(num, session):
    print(f'Get {num}')
    async with session.get("https://httpbin.org/uuid") as response:
        uid = (await response.json()['uuid'])
    print(f"Res {num}: {uid}")

async def faire_toutes_les_requetes_sans_bloquer():
    loop = asyncio.get_event_loop()
    with aiohttp.ClientSession() as session:
        futures = [requete_sans_bloquer(x, session) for x in range(10)]
        loop.run_until_complete(asyncio.gather(*futures))
    loop.close()
    print("Fin de la boucle !")

print("Non bloquant : ")
start = datetime.datetime.now()
faire_toutes_les_requetes_sans_bloquer()
exec_time = (datetime.datetime.now() - start).seconds
print(f"Pour faire 10 requêtes, ça prend {exec_time}s\n")
Run Code Online (Sandbox Code Playgroud)

该代码的第一个经典部分可以正常运行,但是后半部分仅产生:

synchronicite.py:43: RuntimeWarning: coroutine 'faire_toutes_les_requetes_sans_bloquer' was never awaited
Run Code Online (Sandbox Code Playgroud)

fre*_*ish 9

不要在函数loop.run_until_complete内部使用调用async。该方法的目的是在同步上下文中运行异步函数。无论如何,您应该如何更改代码:

async def faire_toutes_les_requetes_sans_bloquer():
    async with aiohttp.ClientSession() as session:
        futures = [requete_sans_bloquer(x, session) for x in range(10)]
        await asyncio.gather(*futures)
    print("Fin de la boucle !")

loop = asyncio.get_event_loop()
loop.run_until_complete(faire_toutes_les_requetes_sans_bloquer())
Run Code Online (Sandbox Code Playgroud)

请注意,单独的faire_toutes_les_requetes_sans_bloquer()调用会创建一个必须通过显式等待await(因为您必须在async上下文中)或传递给某个事件循环的 future。当Python 独自一人时会抱怨这一点。在您的原始代码中,您没有执行任何操作。

  • 这会引发 RuntimeError: This event loop is already running (3认同)

Mar*_*ers 6

通过使用,创建了faire_toutes_les_requetes_sans_bloquer一个等待的函数,协程async def

调用等待函数时,将创建一个新的协程对象。在您等待该函数或将其作为任务运行之前,该函数内的代码将不会运行:

>>> async def foo():
...     print("Running the foo coroutine")
...
>>> foo()
<coroutine object foo at 0x10b186348>
>>> import asyncio
>>> asyncio.run(foo())
Running the foo coroutine
Run Code Online (Sandbox Code Playgroud)

您想使该函数保持同步,因为直到在该函数内部才开始循环:

def faire_toutes_les_requetes_sans_bloquer():
    loop = asyncio.get_event_loop()
    # ...
    loop.close()
    print("Fin de la boucle !")
Run Code Online (Sandbox Code Playgroud)

但是,您还尝试使用一个aiophttp.ClientSession()对象,这是一个异步上下文管理器,您应该将其与一起使用async with,而不仅仅是with与,因此必须在一个等待的任务中运行。如果您使用with的不是async with一个TypeError("Use async with instead"),将引发异常。

你需要移动的各种手段loop.run_until_complete()调用你的faire_toutes_les_requetes_sans_bloquer()功能,所以你可以保持,作为将要运行的主要任务; 您可以asycio.gather()直接致电并等待:

async def faire_toutes_les_requetes_sans_bloquer():
    async with aiohttp.ClientSession() as session:
        futures = [requete_sans_bloquer(x, session) for x in range(10)]
        await asyncio.gather(*futures)
    print("Fin de la boucle !")

print("Non bloquant : ")
start = datetime.datetime.now()
loop.run(faire_toutes_les_requetes_sans_bloquer())
exec_time = (datetime.datetime.now() - start).seconds
print(f"Pour faire 10 requêtes, ça prend {exec_time}s\n")
Run Code Online (Sandbox Code Playgroud)

我使用了新asyncio.run()功能(Python 3.7及更高版本)来运行单个主要任务。这将为该顶层协程创建一个专用循环,并将其运行直至完成。

接下来,您需要)await resp.json()表达式上移动右括号:

uid = (await response.json())['uuid']
Run Code Online (Sandbox Code Playgroud)

您要访问生成的协程而不是协程的'uuid'键。awaitresponse.json()

通过这些更改,您的代码可以运行,但是asyncio版本会在不到一秒钟的时间内完成;您可能需要打印微秒:

exec_time = (datetime.datetime.now() - start).total_seconds()
print(f"Pour faire 10 requêtes, ça prend {exec_time:.3f}s\n")
Run Code Online (Sandbox Code Playgroud)

在我的机器上,同步requests代码在大约4-5秒内完成,而异步代码在0.5秒内完成。