Подтвердить что ты не робот

Asyncio RuntimeError: цикл событий закрыт

Я пытаюсь сделать кучу запросов (~ 1000) с помощью Asyncio и библиотеки aiohttp, но у меня возникает проблема, о которой я не могу найти много информации.

Когда я запускаю этот код с 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()

Трассировку стека можно найти здесь.

Любая помощь или проницательность были бы оценены по достоинству, так как я уже несколько часов стучал головой об этом. Очевидно, это предполагает, что цикл событий был закрыт, который все еще должен быть открытым, но я не вижу, как это возможно.

4b9b3361

Ответ 1

Вы правы, loop.getaddrinfo использует ThreadPoolExecutor для запуска socket.getaddrinfo в потоке.

Вы используете asyncio.wait_for с таймаутом, что означает, что res = yield from asyncio.wait_for... поднимет asyncio.TimeoutError через 4 секунды. Затем сопрограммы get_status возвращают None, и цикл останавливается. Если после этого заканчивается задание, оно попытается запланировать обратный вызов в цикле событий и вызывает исключение, так как оно уже закрыто.

Ответ 2

Ошибка помечена как 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()