我正在尝试学习在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")
代码的第一部分经典可以正确运行,但是后半部分仅产生:
synchronicite.py:43: RuntimeWarning: coroutine 'faire_toutes_les_requetes_sans_bloquer' was never awaited
答案 0 :(得分:2)
您使用faire_toutes_les_requetes_sans_bloquer
将async def
设置为 awaitable 函数(协程)。
调用等待函数时,将创建一个新的协程对象。该函数中的代码要等到您在函数上 await 或将其作为任务运行后才能运行:
>>> async def foo():
... print("Running the foo coroutine")
...
>>> foo()
<coroutine object foo at 0x10b186348>
>>> import asyncio
>>> asyncio.run(foo())
Running the foo coroutine
您要保持该函数同步,因为直到该函数内部您才开始循环:
def faire_toutes_les_requetes_sans_bloquer():
loop = asyncio.get_event_loop()
# ...
loop.close()
print("Fin de la boucle !")
但是,您还尝试使用aiophttp.ClientSession()
对象,这是一个异步上下文管理器,您应该将其与async with
一起使用,而不仅仅是{{ 1}},因此必须在等待任务中运行。如果您使用with
而非with
,则会引发async with
异常。
这一切都意味着您需要将TypeError("Use async with instead")
函数的loop.run_until_complete()
调用移出,因此您可以将其保留为要运行的主要任务。您可以直接在faire_toutes_les_requetes_sans_bloquer()
上呼叫并等待:
asycio.gather()
我使用了新的asyncio.run()
function(Python 3.7及更高版本)来运行单个主要任务。这将为该顶层协程创建一个专用循环,并将其运行直至完成。
接下来,您需要在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")
表达式上移动右括号)
:
await resp.json()
您要访问uid = (await response.json())['uuid']
的结果上的'uuid'
键,而不是await
生成的协程。
有了这些更改,您的代码即可工作,但是asyncio版本会在不到一秒的时间内完成;您可能需要打印微秒:
response.json()
在我的计算机上,同步exec_time = (datetime.datetime.now() - start).total_seconds()
print(f"Pour faire 10 requêtes, ça prend {exec_time:.3f}s\n")
代码在大约4-5秒内完成,而异步代码在.5秒内完成。
答案 1 :(得分:0)
请勿在{{1}}函数内使用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())
调用会创建一个Future,必须通过显式faire_toutes_les_requetes_sans_bloquer()
等待它(为此您必须在await
上下文中)或传递到某个事件循环。当Python独处时,他会抱怨这一点。在您的原始代码中,您什么都不做。