Если создать таск, который запускает другой таск, и отменить первый, выполнится ли второй таск и почему
Что проверяют
Понимаете ли вы модель отмены в asyncio: задачи не образуют дерева, и отмена одной не каскадируется на те, что она создала. Следом почти всегда спрашивают, что изменится, если первая задача ждёт вторую через await, и как это работает с TaskGroup.
Как отвечать
Короткий ответ: да, вторая задача выполнится. asyncio.create_task() сразу ставит корутину в цикл событий как самостоятельную задачу. task_a.cancel() лишь запрашивает отмену: на ближайшем await внутри A возникает CancelledError, и A завершается. B о родителе ничего не знает и работает дальше.
Дальше покажите, где ответ меняется:
- A ждёт B через
await task_b: отмена A передаётся в ожидаемый объект, и B тоже получаетCancelledError. Чтобы защитить B, ожидание оборачивают вasyncio.shield(task_b): A отменится, B доработает; asyncio.gather()иasyncio.wait_for(): при собственной отмене отменяют и то, что ждут;asyncio.TaskGroup(Python 3.11+): структурная конкурентность. Если отменили задачу, внутри которой открытasync with TaskGroup(), группа отменит все свои дочерние задачи и дождётся их. Ту же модель дают nursery в Trio и task group в AnyIO;- завершение программы:
asyncio.run()перед закрытием цикла отменяет все незавершённые задачи. Еслиmainзакончится раньше B, B оборвётся, хотя отмену A она пережила.
Отдельно стоит упомянуть, что цикл событий держит на задачи только слабые ссылки. Результат create_task() сохраняют в переменную или множество, иначе задачу может собрать сборщик мусора посреди работы.
Где спотыкаются
Отвечают «нет, вторая тоже отменится», перенося модель потоков или структурной конкурентности на голый create_task.
Забывают про случай с await: для него ответ противоположный, и именно этот поворот интервьюер чаще всего и проверяет.
Перехватывают CancelledError голым except: и не пробрасывают дальше. Задача продолжает работать, будто отмены не было, а TaskGroup и wait_for ведут себя непредсказуемо. С Python 3.8 это наследник BaseException, поэтому except Exception его не ловит; после очистки ресурсов исключение нужно пробросить через raise.