import asyncio await asyncio.shield(aw, *, loop=None)
aw
- объект задачи,loop=None
- параметр цикла (удален в Python 3.10)Функция shield()
модуля asyncio
защищает объект задачи от отмены методом Task.cancel()
.
Если аргумент aw
является сопрограммой, то она автоматически назначается как задача.
Выражение:
task = asyncio.create_task(something()) res = await shield(task)
эквивалентно:
res = await something()
за исключением того, что если содержащаяся в выражении сопрограмма отменяется через res.cansel()
, то задача, выполняемая в функции something()
не отменяется. С точки зрения something()
отмены не произошло. Хотя вызывающий ее объект отменен, выражение await
по-прежнему вызовет исключение asyncio.CancelledError
.
Концептуально, функция похожа на пуленепробиваемый жилет, который поглощает пулю, защищая владельца, но сам разрушается. Так же и asyncio.shield()
поглощает отмену и при запросе результата сообщает о себе как об отмененной, вызывая исключение asyncio.CancelledError
, но позволяет защищенной задаче продолжать выполнение.
Такое поведение функции гарантирует, что отмена будет "успешной", то есть отменяющая сторона не сможет сказать, что отмену фактически обошли. Это сделано намеренно и в целом делает механизм отмены более последовательным.
Если функция something()
отменяется другими способами, например изнутри себя, то такое поведение также отменит asyncio.shield()
.
Если необходимо полностью игнорировать отмену, что не рекомендуется, то функцию asyncio.shield()
следует обернуть в try/except
, как показано ниже:
task = asyncio.create_task(something()) try: res = await shield(task) except CancelledError: res = None
Важно! Сохраняйте ссылку на задачи, переданные этой функции, чтобы задача не исчезла в процессе выполнения. Цикл событий сохраняет только слабые ссылки на задачи. Задача, на которую больше нигде нет ссылок, может быть удалена сборщиком мусора в любое время, даже до того, как она будет выполнена. Для надежных фоновых задач типа "запустил и забыл" нужно собрать их в коллекцию. Подробнее как это сделать в описании asyncio.create_task()
.
Начиная с Python 3.8: параметр цикла
loop
устарел.Изменено в Python 3.10: аргумент цикла
loop
удален.Устарело, начиная с Python 3.10: выдается предупреждение об устаревании, если
aw
не является объектом, подобнымFuture
, и нет запущенного цикла событий.
import asyncio async def coro(): print('Старт задачи...') await asyncio.sleep(2) print('закончили спать') async def cancel_it(some_task): await asyncio.sleep(0.5) some_task.cancel() print('аннулирование произведено') async def main(): real_task = asyncio.create_task(coro()) shield = asyncio.shield(real_task) # отменяем shield в фоновом режиме, пока ждем asyncio.create_task(cancel_it(shield)) await real_task assert not real_task.cancelled() assert shield.cancelled() if __name__ == '__main__': asyncio.run(main()) # Старт задачи... # аннулирование произведено # закончили спать