Короткий ответ
task.cancel() запрашивает отмену: внутри корутины на следующей точке await поднимается asyncio.CancelledError, которую код задачи должен уметь корректно обработать (освободить ресурсы в finally), не подавляя её без необходимости.
Как это работает подробнее
cancel() не останавливает корутину мгновенно — он лишь планирует выброс CancelledError в точке, где задача сейчас приостановлена на await. Если в коде есть try/finally или async with, блоки очистки всё равно выполнятся при распространении CancelledError.
Пример кода
example-276.py
import asyncio
async def long_running():
try:
while True:
await asyncio.sleep(1)
print("работаю...")
except asyncio.CancelledError:
print("получен сигнал отмены, освобождаю ресурсы")
raise # важно повторно поднять, а не подавить
async def main():
task = asyncio.create_task(long_running())
await asyncio.sleep(2.5)
task.cancel()
try:
await task
except asyncio.CancelledError:
print("задача отменена успешно")
asyncio.run(main())Что использовать на практике
- Практика. После вызова
cancel()обычно дожидаютсяawait task, оборачивая это вtry/except asyncio.CancelledError; важно не «глотать»CancelledErrorвнутри задачи без повторногоraise, так как это нарушает протокол отмены.
Подводные камни
- Риски. Оборачивают всё тело корутины в
except:без типа, неявно перехватывая иCancelledError, из-за чего отменённая задача продолжает работать, будто отмены не было; забываютawaitотменённую задачу, из-за чего исключение остаётся необработанным.