Как связаны event loop, coroutine и future в Python
Что проверяют
Понимаете ли вы, кто в asyncio что делает: корутина описывает работу, future хранит будущий результат, а цикл событий решает, кого и когда продолжить. Следом обычно спрашивают, чем Task отличается от Future и что сделает с циклом блокирующий вызов.
Как отвечать
Разложите по ролям:
- корутина: вызов
async def-функции возвращает объект корутины и ничего не выполняет. Код начнёт работать, только когда корутину ждут черезawaitили оборачивают в задачу; - future: низкоуровневый объект с состоянием (ожидает, готов, отменён), результатом или исключением и списком колбэков. Результат ему выставляет кто-то снаружи;
- task: подкласс
Future, который ведёт корутину. Он прогоняет её до ближайшегоawaitна незавершённом future, подписывается на его завершение и отдаёт управление циклу; - event loop: один поток, очередь готовых колбэков и селектор ввода-вывода (epoll, kqueue). Когда сокет готов, цикл выставляет результат future, тот вызывает колбэк задачи, и корутина продолжается с места
await.
Итог: цикл крутит задачи, задачи крутят корутины, а future это точка встречи ожидания и события.
Практика: запуск через asyncio.run(), задачи через asyncio.create_task(), документация называет его предпочтительным вместо ensure_future. С Python 3.11 есть asyncio.TaskGroup, который отменяет остальные задачи при ошибке одной. Ссылки на задачи нужно хранить: цикл держит их слабо, и задача без ссылки может исчезнуть посреди работы.
Отдельно упомяните concurrent.futures.Future: другой класс, для потоков и процессов, мост к asyncio дают run_in_executor() и asyncio.to_thread().
Где спотыкаются
Думают, что вызов корутины её запускает. Без await она не выполнится, а Python выдаст предупреждение coroutine ... was never awaited.
Вызывают time.sleep() или синхронный requests внутри корутины. Цикл один, и такая функция останавливает все задачи сразу.
Путают asyncio.Future с concurrent.futures.Future или считают, что asyncio даёт параллельные вычисления на нескольких ядрах. Это конкурентность ввода-вывода в одном потоке, для тяжёлых вычислений нужны процессы.