web-dev-qa-db-ja.com

Asyncio RuntimeError:イベントループが閉じています

Asyncioとaiohttpライブラリを使用して大量のリクエスト(〜1000)を作成しようとしていますが、詳細な情報が見つからない問題が発生しています。

このコードを10個のURLで実行すると、問題なく実行されます。 100以上のURLで実行すると、壊れてRuntimeError: Event loop is closedエラー。

import asyncio
import aiohttp


@asyncio.coroutine
def get_status(url):
    code = '000'
    try:
        res = yield from asyncio.wait_for(aiohttp.request('GET', url), 4)
        code = res.status
        res.close()
    except Exception as e:
        print(e)
    print(code)


if __name__ == "__main__":
    urls = ['https://google.com/'] * 100
    coros = [asyncio.Task(get_status(url)) for url in urls]
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait(coros))
    loop.close()

スタックトレースは here にあります。

これから数時間、私はこれについて頭をぶつけてきましたので、どんな助けや洞察もいただければ幸いです。明らかに、これはまだ開かれているはずのイベントループが閉じられたことを示していますが、それがどのように可能かはわかりません。

17
Patrick Allen

そうです、 loop.getaddrinfoThreadPoolExecutorを使用してスレッドでsocket.getaddrinfoを実行します。

asyncio.wait_for をタイムアウトとともに使用しています。つまり、res = yield from asyncio.wait_for...は4秒後にasyncio.TimeoutErrorを発生させます。次に、get_statusコルーチンがNoneを返し、ループが停止します。その後ジョブが終了すると、イベントループでコールバックをスケジュールしようとし、すでに閉じられているため、例外を発生させます。

7
Vincent

バグは https://github.com/python/asyncio/issues/258 として報告されます。

迅速な回避策として、カスタムエグゼキューターを使用することをお勧めします。

loop = asyncio.get_event_loop()
executor = concurrent.futures.ThreadPoolExecutor(5)
loop.set_default_executor(executor)

プログラムを完了する前に

executor.shutdown(wait=True)
loop.close()
18
Andrew Svetlov