1. ホーム
  2. python

[解決済み] asyncioを学習中。「コルーチンが待機していない」警告エラー

2022-02-02 03:50:09

質問

スクリプトを最適化するために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

解決方法は?

あなたが作った faire_toutes_les_requetes_sans_bloquer an 待受画面 関数、コルーチンを使用することで 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

その機能を維持したい 同期 なぜなら、この関数の内部までループを開始しないからです。

def faire_toutes_les_requetes_sans_bloquer():
    loop = asyncio.get_event_loop()
    # ...
    loop.close()
    print("Fin de la boucle !")

しかし、あなたが使おうとしているのも aiophttp.ClientSession() オブジェクトがあり、それが 非同期コンテキスト・マネージャ で使用することが想定されています。 async with だけでなく with そのため、待機可能なタスクの脇で実行されなければならない。もし with の代わりに async with a 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")

私は、新しい asyncio.run() 機能 (Python 3.7 以降) を使って、単一のメインタスクを実行します。これは、そのトップレベルのコルーチン専用のループを作成し、完了するまで実行します。

次に ) の括弧は await resp.json() という式で表されます。

uid = (await response.json())['uuid']

にアクセスしたい。 'uuid' の結果で、キーは await が実行されるコルーチンではなく response.json() を生成します。

これらの変更であなたのコードは動作しますが、asyncioバージョンはサブ秒の時間で終了します; あなたはマイクロ秒を表示したいかもしれません。

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

私のマシンでは、同期的な requests のコードは約4~5秒、asycioのコードは0.5秒未満で完了します。