RuntimeError: этот цикл событий уже запущен в python

Я думаю, что получаю эту ошибку, потому что мой код вызывает asyncio.get_event_loop().run_until_complete(foo()) дважды. Один раз из foo() и второй раз из функции, вызванной foo(). Тогда мой вопрос: почему это должно быть проблемой? Почему мне все равно, что этот цикл работает?


Было внесено изменение в этот вопрос, который, я думаю, скрыл его (некоторые люди предпочитают следовать правилам, не понимая их, поэтому из названия было удалено "незаконное" слово). К сожалению, это создает путаницу.

Я не удивлен тем, что ошибка поднята. Я могу отследить его до источника asyncio и увидеть, что авторы этой библиотеки хотели сделать это таким образом, там нет таинственности. Непонятная часть заключается в том, что авторы библиотеки решили, что незаконно запрашивать из цикла событий выполнение некоторой функции до завершения, когда цикл уже запущен.

Мы можем свести проблему только к двум таким вызовам, и через анализ случаев мы увидим, что это три возможности:

  • Ни одна из двух функций не заканчивается.
  • Одна из функций заканчивается.
  • Обе функции в конечном итоге завершаются.

Теперь, есть ли нормальное поведение, которое будет касаться всех трех случаев? Для меня очевидно, что есть, или, возможно, возможны многочисленные разумные способы поведения. Например:

  • Ничего особенного, выполнение обеих функций чередуется, и они продолжают работать вечно, как и ожидалось.
  • Цикл не возвращает управление коду, следующему первому экземпляру run_until_complete(), пока не завершится вторая функция (таким образом, код после run_until_complete() не будет выполнен.
  • После завершения последней функции цикл возвращает управление первому объекту кода, который вызывает run_until_complete игнорирование всех других сайтов-вызовов.

Теперь я могу понять, что это поведение может быть не тем, что все захотят. Но, поскольку эта библиотека решила дать программистам контроль над запуском/остановкой цикла событий, она также должна отвечать последствиям таких решений. Невозможно запустить один и тот же цикл за один раз, избегая при этом этого кода библиотеки, что снижает качество и полезность библиотек, использующих asyncio (что действительно имеет место, например, aiohttp).

Ответ 1

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

Поскольку у вас есть этот вопрос, я думаю, вы можете неправильно понять, как работает asyncio. Пожалуйста, прочитайте эту статью - она не большая и дает хорошее введение.

Upd:

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

await coro()  # add coro() to be run by event loop blocking flow here until coro() is finished

или создание задачи:

asyncio.ensure_future(coro())  # add coro() to be run by event loop without blocking flow here

Как вы можете видеть, вам не нужны методы цикла событий вызова, чтобы что-то запускалось им.

Метод цикла событий, такой как run_forever или run_until_complete - это всего лишь способ запустить цикл событий в целом.

run_until_complete(foo()) означает: "добавьте foo() для запуска по циклу событий и запускайте сам цикл событий до тех пор, пока foo() не будет завершен".

Ответ 2

Я решил проблему с помощью nest_async

pip install nest_asyncio

и добавив ниже строки в моем файле.

import nest_asyncio
nest_asyncio.apply()

Ответ 3

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

run_until_complete предназначен не для одновременного запуска любого числа произвольных асинхронных функций, а для запуска основной точки входа всей вашей асинхронной программы. Это ограничение не сразу видно из документов.

Поскольку библиотеки, такие как aiohttp, будут ставить в очередь собственную точку входа для запуска в качестве сервера и блокировать циклические синхронные операции, используя run_until_complete или run_forever, цикл событий уже будет запущен, и вы не сможете запускать независимые синхронные операции в этом цикле событий и дождитесь результата в этой теме.

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

Другой способ справиться с этой ситуацией - выполнить ваш код во время запуска и очистить обратные вызовы асинхронной http-библиотеки, которую вы используете. Вот пример того, как вы можете сделать это.