PythonMiddleTechnical
Что такое cancellation в asyncio?
Cancellation — кооперативный запрос задаче завершиться: task.cancel() помечает Task, а CancelledError инжектится в следующей точке await. Очистка делается в finally, и CancelledError нужно повторно пробросить.
Модель cancellation
Отмена в asyncio кооперативная. Task.cancel() не убивает поток или корутину мгновенно — он помечает Task флагом и планирует выброс asyncio.CancelledError в следующей точке await. CPU-bound цикл без await отменить нельзя: пока coroutine не отдаст управление, loop её не трогает.
С Python 3.8 CancelledError наследуется от BaseException, а не Exception — это сделано специально, чтобы except Exception: случайно её не глотал.
Что делать в коде задачи
import asyncio
async def worker():
try:
while True:
await asyncio.sleep(1)
do_work()
except asyncio.CancelledError:
# освобождаем ресурсы синхронно или с await на короткие операции
await close_connection()
raise # ОБЯЗАТЕЛЬНО пробросить, иначе structured concurrency сломается
finally:
cleanup_local_state()
async def main():
task = asyncio.create_task(worker())
await asyncio.sleep(0.1)
task.cancel()
try:
await task
except asyncio.CancelledError:
print("worker cancelled cleanly")
asyncio.run(main())
Как cancellation связан с timeout и TaskGroup
asyncio.wait_for(coro, timeout)внутри используетtask.cancel()по таймеру.asyncio.timeout()(3.11+) делает то же через context manager и корректно обрабатывает shielded scopes.TaskGroupотменяет оставшиеся задачи, если одна упала с исключением.asyncio.shield(coro)защищает coroutine от внешней отмены — отменяется только внешняя обёртка.
Подводные камни
- Глотать
CancelledErrorвнутриexcept Exception(на 3.7 это происходит автоматически) — task видится как успешно завершённая, родитель не узнаёт об отмене. - Делать долгий blocking I/O в
finally— отмена «зависает» пока cleanup не закончится. - В 3.11+ повторный
cancel()увеличиваетcancelling()счётчик;uncancel()нужно вызывать осторожно. - Полагаться, что
task.cancel()остановит CPU-bound цикл без await — не остановит.
Common mistakes
- Думать, что cancel убивает поток.
- Не знать, где возникает CancelledError.
- Не использовать finally для cleanup.
What the interviewer is testing
- Объясняет cooperative cancellation.
- Понимает cleanup и re-raise.
- Знает связь с timeout/TaskGroup.