Что происходит с вложенной таской при отмене главной таски
Что проверяют
Понимаете ли, что create_task порождает независимую задачу, и знаете ли, как именно распространяется отмена. Вопрос отделяет тех, кто писал на asyncio сервис, от тех, кто читал про него статью.
Как отвечать
Отмена не передаётся по факту вложенности, у неё два переносчика:
- если главная задача в момент отмены ожидает дочернюю (
await inner), тоcancel()пробрасывается в то, чего задача ждёт, и дочерняя тоже получаетCancelledError; - если дочернюю запустили через
create_taskи не ждут, с родителем её не связывает ничего: она продолжит работать сиротой и после его завершения.
Поэтому правильный ответ в современном коде это asyncio.TaskGroup: при ошибке или отмене группа отменяет все свои задачи и дожидается их на выходе из async with. До появления групп ту же роль играл gather, отмена которого отменяет всех детей.
Механика самой отмены: cancel() ничего не убивает, он планирует возбуждение CancelledError в ближайшей точке ожидания. Задача обязана дать себя отменить:
try:
await work()
except asyncio.CancelledError:
await cleanup()
raiseГде спотыкаются
Глотают CancelledError: ловят его и не пробрасывают дальше. Задача тогда завершается обычным результатом, снаружи она выглядит не отменённой, а тот, кто её отменял, продолжает ждать завершения, которого по его логике уже не будет.
Второе: ловят отмену через except Exception. CancelledError наследуется от BaseException, такой обработчик её не увидит, и весь код очистки, написанный в расчёте на отмену, молча не выполнится.
Третье: не сохраняют ссылку на объект задачи. Цикл событий держит только слабую ссылку, поэтому задача без внешней ссылки может быть собрана сборщиком мусора посреди работы. Ссылку кладут в множество и убирают в обработчике завершения.