Документация Python неофициальный перевод

asyncio-task.md

1133 строк · 84.3 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/3.15/library/asyncio-task.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# Корутины и задачи89В этом разделе представлены высокоуровневые API asyncio для работы с корутинами и задачами.1011[Корутины](https://python-all.ru/3.15/library/asyncio-task.html#coroutines) [Ожидаемые объекты](https://python-all.ru/3.15/library/asyncio-task.html#awaitables) [Создание задач](https://python-all.ru/3.15/library/asyncio-task.html#creating-tasks) [Отмена задачи](https://python-all.ru/3.15/library/asyncio-task.html#task-cancellation) [Группы задач](https://python-all.ru/3.15/library/asyncio-task.html#task-groups) [Приостановка выполнения](https://python-all.ru/3.15/library/asyncio-task.html#sleeping) [Конкурентное выполнение задач](https://python-all.ru/3.15/library/asyncio-task.html#running-tasks-concurrently) [Фабрика задач с немедленным выполнением](https://python-all.ru/3.15/library/asyncio-task.html#eager-task-factory) [Защита от отмены](https://python-all.ru/3.15/library/asyncio-task.html#shielding-from-cancellation) [Тайм-ауты](https://python-all.ru/3.15/library/asyncio-task.html#timeouts) [Примитивы ожидания](https://python-all.ru/3.15/library/asyncio-task.html#waiting-primitives) [Выполнение в потоках](https://python-all.ru/3.15/library/asyncio-task.html#running-in-threads) [Планирование из других потоков](https://python-all.ru/3.15/library/asyncio-task.html#scheduling-from-other-threads) [Интроспекция](https://python-all.ru/3.15/library/asyncio-task.html#introspection) [Объект задачи](https://python-all.ru/3.15/library/asyncio-task.html#task-object)1213## [Корутины](https://python-all.ru/3.15/library/asyncio-task.html#id2)1415**Исходный код:** [Lib/asyncio/coroutines.py](https://python-all.ru/src/3.15/Lib/asyncio/coroutines.py)1617---1819[Корутины](https://python-all.ru/3.15/glossary.html#term-coroutine), объявленные с помощью синтаксиса async/await, являются предпочтительным способом написания приложений asyncio. Например, следующий фрагмент кода выводит «hello», ждёт 1 секунду, а затем выводит «world»:2021```python22>>> import asyncio2324>>> async def main():25...     print('hello')26...     await asyncio.sleep(1)27...     print('world')2829>>> asyncio.run(main())30hello31world32```3334Обратите внимание, что простой вызов корутины не запланирует её выполнение:3536```python37>>> main()38<coroutine object main at 0x1053bb7c8>39```4041Для непосредственного выполнения корутины asyncio предоставляет следующие механизмы:4243- Функция [`asyncio.run()`](https://python-all.ru/3.15/library/asyncio-runner.html#asyncio.run) для запуска точки входа верхнего уровня – функции «main()» (см. пример выше.)44- Ожидание корутины. Следующий фрагмент кода выведет «hello» после ожидания в 1 секунду, а затем выведет «world» после ожидания *ещё* 2 секунд:4546  ```python47  import asyncio48  import time4950  async def say_after(delay, what):51      await asyncio.sleep(delay)52      print(what)5354  async def main():55      print(f"started at {time.strftime('%X')}")5657      await say_after(1, 'hello')58      await say_after(2, 'world')5960      print(f"finished at {time.strftime('%X')}")6162  asyncio.run(main())63  ```6465  Ожидаемый вывод:6667  ```python68  started at 17:13:5269  hello70  world71  finished at 17:13:5572  ```73- Функция [`asyncio.create_task()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.create_task) для параллельного запуска корутин в качестве asyncio [`Tasks`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Task).7475  Давайте изменим пример выше и запустим две `say_after` корутины *параллельно*:7677  ```python78  async def main():79      task1 = asyncio.create_task(80          say_after(1, 'hello'))8182      task2 = asyncio.create_task(83          say_after(2, 'world'))8485      print(f"started at {time.strftime('%X')}")8687      # Ожидать завершения обеих задач (должно занять88      # около 2 секунд).89      await task190      await task29192      print(f"finished at {time.strftime('%X')}")93  ```9495  Обратите внимание, что ожидаемый вывод теперь показывает, что фрагмент выполняется на 1 секунду быстрее, чем раньше:9697  ```python98  started at 17:14:3299  hello100  world101  finished at 17:14:34102  ```103- Класс [`asyncio.TaskGroup`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.TaskGroup) предоставляет более современную альтернативу [`create_task()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.create_task). С помощью этого API последний пример примет вид:104105  ```python106  async def main():107      async with asyncio.TaskGroup() as tg:108          task1 = tg.create_task(109              say_after(1, 'hello'))110111          task2 = tg.create_task(112              say_after(2, 'world'))113114          print(f"started at {time.strftime('%X')}")115116      # Вызов await выполняется неявно при выходе из контекстного менеджера.117118      print(f"finished at {time.strftime('%X')}")119  ```120121  Время выполнения и вывод должны быть такими же, как в предыдущей версии.122123  Добавлено в версии 3.11: [`asyncio.TaskGroup`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.TaskGroup).124125## [Ожидаемые объекты](https://python-all.ru/3.15/library/asyncio-task.html#id3)126127Говорят, что объект является **ожидаемым** объектом, если его можно использовать в выражении [`await`](https://python-all.ru/3.15/reference/expressions.html#await). Многие API asyncio предназначены для принятия ожидаемых объектов.128129Существует три основных типа *ожидаемых* объектов: **корутины**, **задачи** и **future**.130131Корутины132133Корутины Python являются *ожидаемыми объектами*, а значит, их можно ожидать из других корутин:134135```python136import asyncio137138async def nested():139    return 42140141async def main():142    # Если просто вызвать "nested()", ничего не произойдет.143    # Создается объект корутины, но он не ожидается,144    # поэтому он *вообще не выполнится*.145    nested()  # будет вызвано предупреждение "RuntimeWarning".146147    # Давайте поступим иначе и теперь ожидаем его:148    print(await nested())  # выведет "42".149150asyncio.run(main())151```152153> **Важно**154>155> В данной документации термин «корутина» может использоваться для двух тесно связанных понятий:156>157> - *корутинная функция*: функция [`async def`](https://python-all.ru/3.15/reference/compound_stmts.html#async-def);158> - *объект корутины*: объект, возвращаемый при вызове *корутинной функции*.159160Задачи161162*Задачи* используются для планирования корутин *параллельно*.163164Когда корутина обёрнута в *задачу* с помощью таких функций, как [`asyncio.create_task()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.create_task), корутина автоматически планируется к скорому выполнению:165166```python167import asyncio168169async def nested():170    return 42171172async def main():173    # Запланировать выполнение nested() вскоре конкурентно174    # с "main()".175    task = asyncio.create_task(nested())176177    # "задача" теперь можно использовать для отмены "nested()" или178    # можно просто применить await, чтобы дождаться завершения:179    await task180181asyncio.run(main())182```183184Future185186[`Future`](https://python-all.ru/3.15/library/asyncio-future.html#asyncio.Future) – это специальный **низкоуровневый** ожидаемый объект, который представляет **конечный результат** асинхронной операции.187188Когда объект Future *ожидается*, это означает, что корутина будет ожидать, пока Future не будет разрешён в каком-то другом месте.189190Объекты Future в asyncio необходимы для того, чтобы код, основанный на колбэках, мог использоваться с async/await.191192Обычно **нет необходимости** создавать объекты Future в коде уровня приложения.193194Объекты Future, иногда предоставляемые библиотеками и некоторыми API asyncio, можно ожидать (await):195196```python197async def main():198    await function_that_returns_a_future_object()199200    # это также допустимо:201    await asyncio.gather(202        function_that_returns_a_future_object(),203        some_python_coroutine()204    )205```206207Хороший пример низкоуровневой функции, возвращающей объект Future – [`loop.run_in_executor()`](https://python-all.ru/3.15/library/asyncio-eventloop.html#asyncio.loop.run_in_executor).208209## [Создание задач](https://python-all.ru/3.15/library/asyncio-task.html#id4)210211**Исходный код:** [Lib/asyncio/tasks.py](https://python-all.ru/src/3.15/Lib/asyncio/tasks.py)212213---214215#### `asyncio.create_task(coro, *, name=None, context=None, eager_start=None, **kwargs)`216217Wrap the *coro* [coroutine](https://python-all.ru/3.15/library/asyncio-task.html#coroutine) into a [`Task`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Task) and schedule its execution. Return the Task object.218219Полная сигнатура функции в основном такая же, как у конструктора (или фабрики) [`Task`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Task) – все именованные аргументы этой функции передаются в этот интерфейс.220221Необязательный именованный аргумент *context* позволяет указать пользовательский [`contextvars.Context`](https://python-all.ru/3.15/library/contextvars.html#contextvars.Context) для выполнения *coro*. Копия текущего контекста создаётся, если *context* не передан.222223Необязательный именованный аргумент *eager\_start* позволяет указать, должна ли задача выполняться немедленно при вызове create\_task или быть запланирована позднее. Если *eager\_start* не передан, используется режим, установленный с помощью [`loop.set_task_factory()`](https://python-all.ru/3.15/library/asyncio-eventloop.html#asyncio.loop.set_task_factory).224225Задача выполняется в цикле, возвращаемом [`get_running_loop()`](https://python-all.ru/3.15/library/asyncio-eventloop.html#asyncio.get_running_loop); [`RuntimeError`](https://python-all.ru/3.15/library/exceptions.html#RuntimeError) возбуждается, если в текущем потоке нет запущенного цикла.226227> **Примечание**228>229> [`asyncio.TaskGroup.create_task()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.TaskGroup.create_task) – это новая альтернатива, использующая структурную конкурентность; она позволяет ожидать группу связанных задач с надёжными гарантиями безопасности.230231> **Важно**232>233> Сохраняйте ссылку на результат этой функции, чтобы задача не исчезла во время выполнения. Цикл событий хранит только слабые ссылки на задачи. Задача, на которую нет других ссылок, может быть собрана сборщиком мусора в любой момент, даже до завершения. Для надёжных фоновых задач «запустил и забыл» собирайте их в коллекцию:234>235> ```python236> background_tasks = set()237>238> for i in range(10):239>     task = asyncio.create_task(some_coro(param=i))240>241>     # Добавить задачу в множество. Это создаёт сильную ссылку.242>     background_tasks.add(task)243>244>     # Чтобы предотвратить постоянное хранение ссылок на завершённые задачи,245>     # нужно, чтобы каждая задача удаляла свою собственную ссылку из множества после246>     # завершения:247>     task.add_done_callback(background_tasks.discard)248> ```249250Добавлено в версии 3.7.251252Изменено в версии 3.8: Добавлен параметр *name*.253254Изменено в версии 3.11: Добавлен параметр *context*.255256Изменено в версии 3.14: Добавлен параметр *eager\_start* путём передачи всех *kwargs*.257258## [Отмена задачи](https://python-all.ru/3.15/library/asyncio-task.html#id5)259260Задачи можно легко и безопасно отменять. При отмене задачи в ней при первой же возможности будет возбуждено [`asyncio.CancelledError`](https://python-all.ru/3.15/library/asyncio-exceptions.html#asyncio.CancelledError).261262Рекомендуется, чтобы корутины использовали блоки `try/finally` для надёжного выполнения логики очистки. Если [`asyncio.CancelledError`](https://python-all.ru/3.15/library/asyncio-exceptions.html#asyncio.CancelledError) явно перехватывается, его обычно следует пробрасывать дальше после завершения очистки. `asyncio.CancelledError` напрямую наследует [`BaseException`](https://python-all.ru/3.15/library/exceptions.html#BaseException), поэтому большинству кода не нужно знать о нём.263264Компоненты asyncio, обеспечивающие структурную конкурентность, такие как [`asyncio.TaskGroup`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.TaskGroup) и [`asyncio.timeout()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.timeout), внутренне реализованы с использованием отмены и могут работать некорректно, если корутина подавляет [`asyncio.CancelledError`](https://python-all.ru/3.15/library/asyncio-exceptions.html#asyncio.CancelledError). Аналогично, пользовательскому коду обычно не следует вызывать [`uncancel`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Task.uncancel). Однако в случаях, когда подавление `asyncio.CancelledError` действительно необходимо, нужно также вызвать `uncancel()`, чтобы полностью снять состояние отмены.265266## [Группы задач](https://python-all.ru/3.15/library/asyncio-task.html#id6)267268Группы задач объединяют API создания задач с удобным и надёжным способом ожидания завершения всех задач в группе.269270#### `class asyncio.TaskGroup`271272An [asynchronous context manager](https://python-all.ru/3.15/reference/datamodel.html#async-context-managers) holding a group of tasks. Tasks can be added to the group using [`create_task()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.create_task). All tasks are awaited when the context manager exits.273274Добавлено в версии 3.12.275276#### `create_task(coro, *, name=None, context=None, eager_start=None, **kwargs)`277278Создаёт задачу в этой группе задач. Сигнатура совпадает с [`asyncio.create_task()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.create_task). Если группа задач неактивна (например, ещё не вошли, уже завершена или находится в процессе остановки), указанная `coro` будет закрыта.279280Изменено в версии 3.13: Закрывает указанную корутину, если группа задач неактивна.281282Изменено в версии 3.14: Передаёт все *kwargs* в [`loop.create_task()`](https://python-all.ru/3.15/library/asyncio-eventloop.html#asyncio.loop.create_task)283284#### `cancel()`285286Отменяет группу задач. Это неисключительный досрочный выход из времени жизни группы задач – полезно, когда цель группы достигнута или её услуги больше не нужны.287288[`cancel()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Task.cancel) будет вызван для любых задач в группе, которые ещё не завершены, а также для родительской (тела) группы. Контекстный менеджер группы задач завершится *без* возбуждения [`asyncio.CancelledError`](https://python-all.ru/3.15/library/asyncio-exceptions.html#asyncio.CancelledError).289290Если `cancel()` вызывается до входа в группу задач, группа будет отменена при входе. Это полезно для сценариев, когда один фрагмент кода передаёт неиспользуемый экземпляр [`asyncio.TaskGroup`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.TaskGroup) другому, чтобы иметь возможность отменить всё, выполняемое внутри группы.291292`cancel()` идемпотентен и может быть вызван после того, как группа задач уже завершена.293294Некоторые способы использования `cancel()`:295296- вызвать его из тела группы задач на основе какого-либо условия или события297- передать экземпляр группы задач дочерним задачам через [`create_task()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.create_task), что позволяет дочерней задаче условно отменить всю группу298- передать экземпляр группы задач или привязанный метод `cancel()` другой задаче *до* открытия группы задач, что позволяет удалённую отмену299300Добавлено в версии 3.15.301302Пример:303304```python305async def main():306    async with asyncio.TaskGroup() as tg:307        task1 = tg.create_task(some_coro(...))308        task2 = tg.create_task(another_coro(...))309    print(f"Both tasks have completed now: {task1.result()}, {task2.result()}")310```311312Оператор `async with` ожидает завершения всех задач в группе. Во время ожидания в группу всё ещё можно добавлять новые задачи (например, передав `tg` в одну из корутин и вызвав `tg.create_task()` в этой корутине). Также есть возможность запросить завершение всей группы задач с помощью `tg.cancel()`, исходя из некоторого условия. Как только последняя задача завершена и блок `async with` завершён, добавлять новые задачи в группу нельзя.313314Когда одна из задач, принадлежащих группе, впервые завершается с ошибкой с исключением, отличным от [`asyncio.CancelledError`](https://python-all.ru/3.15/library/asyncio-exceptions.html#asyncio.CancelledError), оставшиеся задачи в группе отменяются. После этого в группу нельзя добавлять новые задачи. Если в этот момент тело оператора `async with` всё ещё активно (т.е. [`__aexit__()`](https://python-all.ru/3.15/reference/datamodel.html#object.__aexit__) ещё не был вызван), то задача, непосредственно содержащая оператор `async with`, также отменяется. Возникшее `asyncio.CancelledError` прервёт `await`, но не всплывёт за пределы содержащего оператора `async with`.315316Когда все задачи завершены, если какие-либо задачи завершились ошибкой с исключением, отличным от [`asyncio.CancelledError`](https://python-all.ru/3.15/library/asyncio-exceptions.html#asyncio.CancelledError), эти исключения объединяются в [`ExceptionGroup`](https://python-all.ru/3.15/library/exceptions.html#ExceptionGroup) или [`BaseExceptionGroup`](https://python-all.ru/3.15/library/exceptions.html#BaseExceptionGroup) (в зависимости от ситуации; смотрите их документацию), которое затем возбуждается.317318Два базовых исключения обрабатываются особым образом: Если какая-либо задача завершается ошибкой с [`KeyboardInterrupt`](https://python-all.ru/3.15/library/exceptions.html#KeyboardInterrupt) или [`SystemExit`](https://python-all.ru/3.15/library/exceptions.html#SystemExit), группа задач всё равно отменяет оставшиеся задачи и ожидает их, но затем первоначальное `KeyboardInterrupt` или `SystemExit` возбуждается повторно вместо [`ExceptionGroup`](https://python-all.ru/3.15/library/exceptions.html#ExceptionGroup) или [`BaseExceptionGroup`](https://python-all.ru/3.15/library/exceptions.html#BaseExceptionGroup).319320Если тело оператора `async with` завершается исключением (т.е. [`__aexit__()`](https://python-all.ru/3.15/reference/datamodel.html#object.__aexit__) вызывается с установленным исключением), это обрабатывается так же, как если бы одна из задач завершилась ошибкой: оставшиеся задачи отменяются и ожидаются, а исключения, не связанные с отменой, группируются в gруппу исключений и возбуждаются. Исключение, переданное в `__aexit__()`, если только оно не является [`asyncio.CancelledError`](https://python-all.ru/3.15/library/asyncio-exceptions.html#asyncio.CancelledError), также включается в группу исключений. Тот же особый случай применяется для [`KeyboardInterrupt`](https://python-all.ru/3.15/library/exceptions.html#KeyboardInterrupt) и [`SystemExit`](https://python-all.ru/3.15/library/exceptions.html#SystemExit), как в предыдущем абзаце.321322Группы задач аккуратно не смешивают внутреннюю отмену, используемую для «пробуждения» их [`__aexit__()`](https://python-all.ru/3.15/reference/datamodel.html#object.__aexit__), с запросами на отмену задачи, в которой они выполняются, поступающими от других сторон. В частности, когда одна группа задач синтаксически вложена в другую, и обе одновременно сталкиваются с исключением в одной из своих дочерних задач, внутренняя группа задач обрабатывает свои исключения, а затем внешняя группа задач получает ещё одну отмену и обрабатывает собственные исключения.323324В случае, когда группа задач отменяется извне и также должна возбудить [`ExceptionGroup`](https://python-all.ru/3.15/library/exceptions.html#ExceptionGroup), она вызывает метод [`cancel()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Task.cancel) родительской задачи. Это гарантирует, что [`asyncio.CancelledError`](https://python-all.ru/3.15/library/asyncio-exceptions.html#asyncio.CancelledError) будет возбуждено при следующем [`await`](https://python-all.ru/3.15/reference/expressions.html#await), поэтому отмена не теряется.325326Группы задач сохраняют счётчик отмен, сообщаемый [`asyncio.Task.cancelling()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Task.cancelling).327328Изменено в версии 3.13: Улучшена обработка одновременных внутренних и внешних отмен и корректное сохранение счётчиков отмен.329330## [Ожидание](https://python-all.ru/3.15/library/asyncio-task.html#id7)331332#### `async asyncio.sleep(delay, result=None)`333334Приостанавливает выполнение на *delay* секунд.335336Если *result* указан, он возвращается вызывающей стороне после завершения корутины.337338`sleep()` всегда приостанавливает текущую задачу, позволяя выполняться другим задачам.339340Установка задержки в 0 обеспечивает оптимизированный путь, позволяющий выполняться другим задачам. Это может использоваться длительными функциями, чтобы избежать блокировки цикла событий на всё время вызова функции.341342Пример корутины, выводящей текущую дату каждую секунду в течение 5 секунд:343344```python345import asyncio346import datetime as dt347348async def display_date():349    loop = asyncio.get_running_loop()350    end_time = loop.time() + 5.0351    while True:352        print(dt.datetime.now())353        if (loop.time() + 1.0) >= end_time:354            break355        await asyncio.sleep(1)356357asyncio.run(display_date())358```359360Изменено в версии 3.10: Удалён параметр *loop*.361362Изменено в версии 3.13: Возбуждает [`ValueError`](https://python-all.ru/3.15/library/exceptions.html#ValueError), если *delay* равен [`nan`](https://python-all.ru/3.15/library/math.html#math.nan).363364## [Одновременное выполнение задач](https://python-all.ru/3.15/library/asyncio-task.html#id8)365366#### `awaitable asyncio.gather(*aws, return_exceptions=False)`367368Выполняет [ожидаемые объекты](https://python-all.ru/3.15/library/asyncio-task.html#asyncio-awaitables) из последовательности *aws* *одновременно*.369370Если любой ожидаемый объект из *aws* является корутиной, он автоматически планируется как задача.371372Если все ожидаемые объекты завершены успешно, результатом является сводный список возвращённых значений. Порядок значений результата соответствует порядку ожидаемых объектов в *aws*.373374Если *return\_exceptions* равно `False` (по умолчанию), первое возбуждённое исключение немедленно распространяется на задачу, ожидающую `gather()`. Остальные ожидаемые объекты в последовательности *aws* **не будут отменены** и продолжат выполнение.375376Если *return\_exceptions* равно `True`, исключения обрабатываются так же, как успешные результаты, и объединяются в списке результатов.377378Если `gather()` *отменён*, все переданные ожидаемые объекты (которые ещё не завершились) также *отменяются*.379380Если любая задача или Future из последовательности *aws* *отменена*, это рассматривается так, как если бы она возбудила [`CancelledError`](https://python-all.ru/3.15/library/asyncio-exceptions.html#asyncio.CancelledError) – вызов `gather()` в этом случае **не** отменяется. Это сделано для предотвращения ситуации, когда отмена одной переданной задачи или Future приводит к отмене других задач или Future.381382> **Примечание**383>384> Новая альтернатива для создания и одновременного выполнения задач и ожидания их завершения – это [`asyncio.TaskGroup`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.TaskGroup). *TaskGroup* предоставляет более строгие гарантии безопасности, чем *gather*, для планирования вложенных подзадач: если задача (или подзадача, задача, запланированная задачей) возбуждает исключение, *TaskGroup* отменит остальные запланированные задачи, тогда как *gather* – нет.385386Пример:387388```python389import asyncio390391async def factorial(name, number):392    f = 1393    for i in range(2, number + 1):394        print(f"Task {name}: Compute factorial({number}), currently i={i}...")395        await asyncio.sleep(1)396        f *= i397    print(f"Task {name}: factorial({number}) = {f}")398    return f399400async def main():401    # Запланировать три вызова *конкурентно*:402    L = await asyncio.gather(403        factorial("A", 2),404        factorial("B", 3),405        factorial("C", 4),406    )407    print(L)408409asyncio.run(main())410411# Ожидаемый вывод:412#413#     Задача A: вычисление factorial(2), сейчас i=2...414#     Задача B: вычисление factorial(3), сейчас i=2...415#     Задача C: вычисление factorial(4), сейчас i=2...416#     Задача A: factorial(2) = 2417#     Задача B: вычисление factorial(3), сейчас i=3...418#     Задача C: вычисление factorial(4), сейчас i=3...419#     Задача B: factorial(3) = 6420#     Задача C: вычисление factorial(4), сейчас i=4...421#     Задача C: factorial(4) = 24422#     [2, 6, 24]423```424425> **Примечание**426>427> Если *return\_exceptions* равно false, отмена gather() после того, как она помечена как завершённая, не приведёт к отмене каких-либо переданных ожидаемых объектов. Например, gather может быть помечена как завершённая после распространения исключения вызывающей стороне, поэтому вызов `gather.cancel()` после перехвата исключения (возбуждённого одним из ожидаемых объектов) из gather не отменит никакие другие ожидаемые объекты.428429Изменено в версии 3.7: Если сам *gather* отменён, отмена распространяется независимо от *return\_exceptions*.430431Изменено в версии 3.10: Удалён параметр *loop*.432433Устарело с версии 3.10: Предупреждение об устаревании выдается, если не предоставлены позиционные аргументы или не все позиционные аргументы являются объектами, подобными Future, и нет запущенного цикла событий.434435## [Фабрика задач с немедленным выполнением](https://python-all.ru/3.15/library/asyncio-task.html#id9)436437#### `asyncio.eager_task_factory(loop, coro, *, name=None, context=None)`438439Фабрика задач для немедленного выполнения.440441При использовании этой фабрики (через [`loop.set_task_factory(asyncio.eager_task_factory)`](https://python-all.ru/3.15/library/asyncio-eventloop.html#asyncio.loop.set_task_factory)) корутины начинают выполняться синхронно во время создания [`Task`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Task). Задачи планируются в цикле событий только в том случае, если они блокируются. Это может повысить производительность, поскольку для корутин, завершающихся синхронно, исключаются накладные расходы на планирование в цикле.442443Типичный пример, где это полезно, – корутины, использующие кэширование или мемоизацию для избежания фактического ввода-вывода, когда это возможно.444445> **Примечание**446>447> Немедленное выполнение корутины – это семантическое изменение. Если корутина завершается возвратом или возбуждением исключения, задача никогда не планируется в цикле событий. Если выполнение корутины блокируется, задача планируется в цикле событий. Это изменение может привести к изменениям в поведении существующих приложений. Например, порядок выполнения задач в приложении, скорее всего, изменится.448449Добавлено в версии 3.12.450451#### `asyncio.create_eager_task_factory(custom_task_constructor)`452453Создает фабрику задач с немедленным выполнением, аналогичную [`eager_task_factory()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.eager_task_factory), используя переданный *custom\_task\_constructor* при создании новой задачи вместо стандартного [`Task`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Task).454455*custom\_task\_constructor* должен быть *вызываемым объектом* с сигнатурой, соответствующей сигнатуре [`Task.__init__`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Task). Вызываемый объект должен возвращать объект, совместимый с [`asyncio.Task`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Task).456457Эта функция возвращает *вызываемый объект*, предназначенный для использования в качестве фабрики задач цикла событий через [`loop.set_task_factory(factory)`](https://python-all.ru/3.15/library/asyncio-eventloop.html#asyncio.loop.set_task_factory)).458459Добавлено в версии 3.12.460461## [Защита от отмены](https://python-all.ru/3.15/library/asyncio-task.html#id10)462463#### `awaitable asyncio.shield(aw)`464465Защищает [ожидаемый объект](https://python-all.ru/3.15/library/asyncio-task.html#asyncio-awaitables) от [`cancelled`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Task.cancel).466467Если *aw* является корутиной, она автоматически планируется как задача.468469Выражение:470471```python472task = asyncio.create_task(something())473res = await shield(task)474```475476эквивалентно:477478```python479res = await something()480```481482*За исключением* того, что если содержащая его корутина отменяется, задача, выполняющаяся в `something()`, не отменяется. С точки зрения `something()` отмена не произошла. Хотя её вызывающий код всё ещё отменён, поэтому выражение «await» по-прежнему возбуждает [`CancelledError`](https://python-all.ru/3.15/library/asyncio-exceptions.html#asyncio.CancelledError).483484Если `something()` отменяется другими средствами (т.е. изнутри самой себя), это также отменит `shield()`.485486Если требуется полностью игнорировать отмену (не рекомендуется), функцию `shield()` следует комбинировать с конструкцией try/except следующим образом:487488```python489task = asyncio.create_task(something())490try:491    res = await shield(task)492except CancelledError:493    res = None494```495496> **Важно**497>498> Сохраняйте ссылку на задачи, переданные этой функции, чтобы избежать исчезновения задачи во время выполнения. Цикл событий хранит только слабые ссылки на задачи. Задача, на которую нет других ссылок, может быть собрана сборщиком мусора в любой момент, даже до её завершения.499500Изменено в версии 3.10: Удалён параметр *loop*.501502Устарело с версии 3.10: Предупреждение об устаревании выдаётся, если *aw* не является объектом, подобным Future, и нет запущенного цикла событий.503504## [Тайм-ауты](https://python-all.ru/3.15/library/asyncio-task.html#id11)505506#### `asyncio.timeout(delay)`507508Возвращает [асинхронный менеджер контекста](https://python-all.ru/3.15/reference/datamodel.html#async-context-managers), который можно использовать для ограничения времени ожидания чего-либо.509510*delay* может быть `None` или числом с плавающей точкой или целым числом секунд ожидания. Если *delay* равен `None`, ограничение по времени не применяется; это может быть полезно, если delay неизвестен на момент создания менеджера контекста.511512В любом случае, менеджер контекста можно перенастроить после создания с помощью [`Timeout.reschedule()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Timeout.reschedule).513514Пример:515516```python517async def main():518    async with asyncio.timeout(10):519        await long_running_task()520```521522Если выполнение `long_running_task` занимает более 10 секунд, менеджер контекста отменяет текущую задачу и обрабатывает возникшее [`asyncio.CancelledError`](https://python-all.ru/3.15/library/asyncio-exceptions.html#asyncio.CancelledError) внутренне, преобразуя его в [`TimeoutError`](https://python-all.ru/3.15/library/exceptions.html#TimeoutError), которое можно перехватить и обработать.523524> **Примечание**525>526> Менеджер контекста [`asyncio.timeout()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.timeout) преобразует [`asyncio.CancelledError`](https://python-all.ru/3.15/library/asyncio-exceptions.html#asyncio.CancelledError) в [`TimeoutError`](https://python-all.ru/3.15/library/exceptions.html#TimeoutError), что означает, что `TimeoutError` может быть перехвачено только *за пределами* менеджера контекста.527528Пример перехвата [`TimeoutError`](https://python-all.ru/3.15/library/exceptions.html#TimeoutError):529530```python531async def main():532    try:533        async with asyncio.timeout(10):534            await long_running_task()535    except TimeoutError:536        print("The long operation timed out, but we've handled it.")537538    print("This statement will run regardless.")539```540541Контекстный менеджер, получаемый с помощью [`asyncio.timeout()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.timeout), может быть перенесён на другой срок и проверен.542543#### `class asyncio.Timeout(when)`544545[Асинхронный контекстный менеджер](https://python-all.ru/3.15/reference/datamodel.html#async-context-managers) для отмены просроченных корутин.546547Предпочтительнее использовать [`asyncio.timeout()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.timeout) или [`asyncio.timeout_at()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.timeout_at) вместо прямого создания экземпляра `Timeout`.548549`when` должно быть абсолютным моментом времени, когда контекст должен сработать по таймауту, измеряемым по часам цикла событий:550551- Если `when` равно `None`, таймаут никогда не сработает.552- Если `when < loop.time()`, таймаут сработает на следующей итерации цикла событий.553554> #### `when() → float | None`555>556> Возвращает текущий срок, или `None`, если текущий срок не задан.557>558> #### `reschedule(when: float | None)`559>560> Перенести таймаут.561>562> #### `expired() → bool`563>564> Возвращает, превысил ли контекстный менеджер свой срок (истек).565566Пример:567568```python569async def main():570    try:571        # В начале мы не знаем тайм-аут, поэтому передаём ``None``.572        async with asyncio.timeout(None) as cm:573            # Теперь мы знаем тайм-аут, поэтому перепланируем его.574            new_deadline = get_running_loop().time() + 10575            cm.reschedule(new_deadline)576577            await long_running_task()578    except TimeoutError:579        pass580581    if cm.expired():582        print("Looks like we haven't finished on time.")583```584585Контекстные менеджеры таймаута можно безопасно вкладывать друг в друга.586587Добавлено в версии 3.12.588589#### `asyncio.timeout_at(when)`590591Похоже на [`asyncio.timeout()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.timeout), за исключением того, что *when* – это абсолютное время остановки ожидания, или `None`.592593Пример:594595```python596async def main():597    loop = get_running_loop()598    deadline = loop.time() + 20599    try:600        async with asyncio.timeout_at(deadline):601            await long_running_task()602    except TimeoutError:603        print("The long operation timed out, but we've handled it.")604605    print("This statement will run regardless.")606```607608Добавлено в версии 3.12.609610#### `async asyncio.wait_for(aw, timeout)`611612Ожидает завершения *aw* [ожидаемого объекта](https://python-all.ru/3.15/library/asyncio-task.html#asyncio-awaitables) с таймаутом.613614Если *aw* является корутиной, она автоматически планируется как задача.615616*timeout* может быть либо `None`, либо числом с плавающей запятой или целым числом секунд ожидания. Если *timeout* равно `None`, блокирует выполнение до завершения future.617618Если происходит таймаут, он отменяет задачу и возбуждает [`TimeoutError`](https://python-all.ru/3.15/library/exceptions.html#TimeoutError).619620Чтобы избежать задачи [`cancellation`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Task.cancel), оберните её в [`shield()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.shield).621622Функция будет ждать, пока future не будет фактически отменена, поэтому общее время ожидания может превысить *timeout*. Если во время отмены произойдёт исключение, оно будет распространено.623624Если ожидание отменяется, future *aw* также отменяется.625626Пример:627628```python629async def eternity():630    # Спать в течение часа631    await asyncio.sleep(3600)632    print('yay!')633634async def main():635    # Ждать не более 1 секунды636    try:637        await asyncio.wait_for(eternity(), timeout=1.0)638    except TimeoutError:639        print('timeout!')640641asyncio.run(main())642643# Ожидаемый вывод:644#645#     тайм-аут!646```647648Изменено в версии 3.7: Когда *aw* отменяется из-за таймаута, `wait_for` ожидает отмены *aw*. Ранее оно немедленно вызывало [`TimeoutError`](https://python-all.ru/3.15/library/exceptions.html#TimeoutError).649650Изменено в версии 3.10: Удалён параметр *loop*.651652Изменено в версии 3.11: Возбуждает [`TimeoutError`](https://python-all.ru/3.15/library/exceptions.html#TimeoutError) вместо [`asyncio.TimeoutError`](https://python-all.ru/3.15/library/asyncio-exceptions.html#asyncio.TimeoutError).653654## [Примитивы ожидания](https://python-all.ru/3.15/library/asyncio-task.html#id12)655656#### `async asyncio.wait(aws, *, timeout=None, return_when=ALL_COMPLETED)`657658Выполняет экземпляры [`Future`](https://python-all.ru/3.15/library/asyncio-future.html#asyncio.Future) и [`Task`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Task) из итерируемого объекта *aws* параллельно и блокирует выполнение до наступления условия, указанного параметром *return\_when*.659660Итерируемый объект *aws* не должен быть пустым.661662Возвращает два набора задач/future: `(done, pending)`.663664Использование:665666```python667done, pending = await asyncio.wait(aws)668```669670*timeout* (число с плавающей точкой или целое) позволяет указать максимальное время ожидания в секундах перед возвратом результата.671672Обратите внимание, что эта функция не вызывает [`TimeoutError`](https://python-all.ru/3.15/library/exceptions.html#TimeoutError). Объекты Future или задачи, не завершённые к моменту таймаута, просто возвращаются во втором наборе.673674*return\_when* указывает, когда эта функция должна вернуть результат. Это должна быть одна из следующих констант:675676| Константа | Описание |677| --- | --- |678| asyncio.FIRST\_COMPLETED | Функция возвращает результат, когда любой future завершается или отменяется. |679| asyncio.FIRST\_EXCEPTION | Функция вернёт результат, когда любой future завершится, возбудив исключение. Если ни один future не возбудит исключение, то это эквивалентно [`ALL_COMPLETED`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.ALL_COMPLETED). |680| asyncio.ALL\_COMPLETED | Функция вернёт результат, когда все futures завершатся или будут отменены. |681682В отличие от [`wait_for()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.wait_for), `wait()` не отменяет futures при возникновении таймаута.683684Изменено в версии 3.10: Удалён параметр *loop*.685686Изменено в версии 3.11: Запрещена прямая передача объектов корутин в `wait()`.687688Изменено в версии 3.12: Добавлена поддержка генераторов, возвращающих задачи.689690#### `asyncio.as_completed(aws, *, timeout=None)`691692Запускает [ожидаемые объекты](https://python-all.ru/3.15/library/asyncio-task.html#asyncio-awaitables) из итерируемого *aws* одновременно. Возвращаемый объект можно итерировать для получения результатов этих ожидаемых объектов по мере их завершения.693694Объект, возвращаемый `as_completed()`, можно итерировать как [асинхронный итератор](https://python-all.ru/3.15/glossary.html#term-asynchronous-iterator) или как обычный [итератор](https://python-all.ru/3.15/glossary.html#term-iterator). При асинхронной итерации изначально переданные ожидаемые объекты возвращаются, если они являются задачами или future. Это упрощает сопоставление ранее запланированных задач с их результатами. Пример:695696```python697ipv4_connect = create_task(open_connection("127.0.0.1", 80))698ipv6_connect = create_task(open_connection("::1", 80))699tasks = [ipv4_connect, ipv6_connect]700701async for earliest_connect in as_completed(tasks):702    # earliest_connect завершён. Результат можно получить703    # с помощью await или вызова earliest_connect.result()704    reader, writer = await earliest_connect705706    if earliest_connect is ipv6_connect:707        print("IPv6 connection established.")708    else:709        print("IPv4 connection established.")710```711712Во время асинхронной итерации для переданных ожидаемых объектов, не являющихся задачами или future, будут возвращаться неявно созданные задачи.713714При использовании в качестве обычного итератора каждая итерация возвращает новую корутину, которая возвращает результат или возбуждает исключение следующего завершённого ожидаемого объекта. Такой подход совместим с версиями Python старше 3.13:715716```python717ipv4_connect = create_task(open_connection("127.0.0.1", 80))718ipv6_connect = create_task(open_connection("::1", 80))719tasks = [ipv4_connect, ipv6_connect]720721for next_connect in as_completed(tasks):722    # next_connect не является одним из исходных объектов задач. Его необходимо723    # ожидать (await) для получения значения результата или возбуждения исключения724    # ожидаемый объект, который завершится следующим.725    reader, writer = await next_connect726```727728Исключение [`TimeoutError`](https://python-all.ru/3.15/library/exceptions.html#TimeoutError) возбуждается, если таймаут наступает до завершения всех ожидаемых объектов. Оно возбуждается в цикле `async for` во время асинхронной итерации или корутинами, возвращаемыми при обычной итерации.729730Изменено в версии 3.10: Удалён параметр *loop*.731732Устарело с версии 3.10: Выдаётся предупреждение об устаревании, если не все ожидаемые объекты в итерируемом *aws* являются объектами, подобными Future, и отсутствует запущенный цикл событий.733734Изменено в версии 3.12: Добавлена поддержка генераторов, возвращающих задачи.735736Изменено в версии 3.13: Результат теперь можно использовать как [асинхронный итератор](https://python-all.ru/3.15/glossary.html#term-asynchronous-iterator), так и как обычный [итератор](https://python-all.ru/3.15/glossary.html#term-iterator) (ранее это был только обычный итератор).737738## [Выполнение в потоках](https://python-all.ru/3.15/library/asyncio-task.html#id13)739740#### `async asyncio.to_thread(func, /, *args, **kwargs)`741742Асинхронно запускает функцию *func* в отдельном потоке.743744Любые \*args и \*\*kwargs, переданные в эту функцию, напрямую передаются в *func*. Также распространяется текущий [`contextvars.Context`](https://python-all.ru/3.15/library/contextvars.html#contextvars.Context), что позволяет обращаться к контекстным переменным из потока цикла событий в отдельном потоке.745746Возвращает корутину, которую можно ожидать для получения конечного результата *func*.747748Эта корутинная функция в первую очередь предназначена для выполнения функций/методов, связанных с вводом-выводом, которые в противном случае заблокировали бы цикл событий, если бы выполнялись в главном потоке. Например:749750```python751def blocking_io():752    print(f"start blocking_io at {time.strftime('%X')}")753    # Обратите внимание, что time.sleep() можно заменить любой блокирующей754    # операцией ввода-вывода, например файловыми операциями.755    time.sleep(1)756    print(f"blocking_io complete at {time.strftime('%X')}")757758async def main():759    print(f"started main at {time.strftime('%X')}")760761    await asyncio.gather(762        asyncio.to_thread(blocking_io),763        asyncio.sleep(1))764765    print(f"finished main at {time.strftime('%X')}")766767asyncio.run(main())768769# Ожидаемый вывод:770#771# запущен main в 19:50:53772# запуск blocking_io в 19:50:53773# blocking_io завершён в 19:50:54774# main завершён в 19:50:54775```776777Прямой вызов `blocking_io()` в любой корутине заблокирует цикл событий на всё время выполнения, добавив 1 секунду ко времени работы. Вместо этого, используя `asyncio.to_thread()`, можно выполнить его в отдельном потоке без блокировки цикла событий.778779> **Примечание**780>781> Из-за [GIL](https://python-all.ru/3.15/glossary.html#term-GIL) `asyncio.to_thread()` обычно можно использовать только для того, чтобы сделать функции, связанные с вводом-выводом, неблокирующими. Однако для модулей расширения, которые освобождают GIL, или альтернативных реализаций Python, у которых его нет, `asyncio.to_thread()` также можно использовать для функций, связанных с процессором.782783Добавлено в версии 3.9.784785## [Планирование из других потоков](https://python-all.ru/3.15/library/asyncio-task.html#id14)786787#### `asyncio.run_coroutine_threadsafe(coro, loop)`788789Отправляет корутину в указанный цикл событий. Потокобезопасно.790791Возвращает [`concurrent.futures.Future`](https://python-all.ru/3.15/library/concurrent.futures.html#concurrent.futures.Future) для ожидания результата из другого потока операционной системы.792793Эта функция предназначена для вызова из другого потока операционной системы, отличного от того, в котором работает цикл событий. Пример:794795```python796def in_thread(loop: asyncio.AbstractEventLoop) -> None:797    # Запустить блокирующий ввод-вывод798    pathlib.Path("example.txt").write_text("hello world", encoding="utf8")799800    # Создать корутину801    coro = asyncio.sleep(1, result=3)802803    # Отправить корутину в заданный цикл804    future = asyncio.run_coroutine_threadsafe(coro, loop)805806    # Ожидать результат с необязательным аргументом таймаута807    assert future.result(timeout=2) == 3808809async def amain() -> None:810    # Получить текущий цикл811    loop = asyncio.get_running_loop()812813    # Запустить что-то в потоке814    await asyncio.to_thread(in_thread, loop)815```816817Также возможна обратная ситуация. Пример:818819```python820@contextlib.contextmanager821def loop_in_thread() -> Generator[asyncio.AbstractEventLoop]:822    loop_fut = concurrent.futures.Future[asyncio.AbstractEventLoop]()823    stop_event = asyncio.Event()824825    async def main() -> None:826        loop_fut.set_result(asyncio.get_running_loop())827        await stop_event.wait()828829    with concurrent.futures.ThreadPoolExecutor(1) as tpe:830        complete_fut = tpe.submit(asyncio.run, main())831        for fut in concurrent.futures.as_completed((loop_fut, complete_fut)):832            if fut is loop_fut:833                loop = loop_fut.result()834                try:835                    yield loop836                finally:837                    loop.call_soon_threadsafe(stop_event.set)838            else:839                fut.result()840841# Создать цикл в другом потоке842with loop_in_thread() as loop:843    # Создать корутину844    coro = asyncio.sleep(1, result=3)845846    # Отправить корутину в заданный цикл847    future = asyncio.run_coroutine_threadsafe(coro, loop)848849    # Ожидать результат с необязательным аргументом таймаута850    assert future.result(timeout=2) == 3851```852853Если в корутине возникло исключение, возвращаемый объект Future получит уведомление. Его также можно использовать для отмены задачи в цикле событий:854855```python856try:857    result = future.result(timeout)858except TimeoutError:859    print('The coroutine took too long, cancelling the task...')860    future.cancel()861except Exception as exc:862    print(f'The coroutine raised an exception: {exc!r}')863else:864    print(f'The coroutine returned: {result!r}')865```866867См. раздел [concurrency and multithreading](https://python-all.ru/3.15/library/asyncio-dev.html#asyncio-multithreading) документации.868869В отличие от других функций asyncio, эта функция требует явной передачи аргумента *loop* .870871Добавлено в версии 3.5.1.872873## [Интроспекция](https://python-all.ru/3.15/library/asyncio-task.html#id15)874875#### `asyncio.current_task(loop=None)`876877Возвращает экземпляр текущей выполняющейся [`Task`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Task) или `None`, если нет выполняющейся задачи.878879Если *loop* равен `None`, используется [`get_running_loop()`](https://python-all.ru/3.15/library/asyncio-eventloop.html#asyncio.get_running_loop) для получения текущего цикла.880881Добавлено в версии 3.7.882883#### `asyncio.all_tasks(loop=None)`884885Возвращает набор ещё не завершённых объектов [`Task`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Task), выполняемых циклом.886887Если *loop* равен `None`, [`get_running_loop()`](https://python-all.ru/3.15/library/asyncio-eventloop.html#asyncio.get_running_loop) используется для получения текущего цикла.888889Добавлено в версии 3.7.890891#### `asyncio.iscoroutine(obj)`892893Возвращает `True`, если *obj* является объектом корутины.894895Добавлено в версии 3.4.896897## [Объект задачи](https://python-all.ru/3.15/library/asyncio-task.html#id16)898899#### `class asyncio.Task(coro, *, loop=None, name=None, context=None, eager_start=False)`900901Объект [`Future-like`](https://python-all.ru/3.15/library/asyncio-future.html#asyncio.Future), выполняющий корутину Python [корутину](https://python-all.ru/3.15/library/asyncio-task.html#coroutine). Непотокобезопасный.902903Задачи используются для выполнения корутин в циклах событий. Если корутина ожидает Future, задача приостанавливает выполнение корутины и ждёт завершения Future. Когда Future *завершён*, выполнение обёрнутой корутины возобновляется.904905Циклы событий используют кооперативное планирование: цикл событий выполняет по одной задаче за раз. Пока задача ожидает завершения Future, цикл событий выполняет другие задачи, колбэки или производит операции ввода-вывода.906907Используйте высокоуровневую функцию [`asyncio.create_task()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.create_task) для создания задач или низкоуровневые функции [`loop.create_task()`](https://python-all.ru/3.15/library/asyncio-eventloop.html#asyncio.loop.create_task) или [`ensure_future()`](https://python-all.ru/3.15/library/asyncio-future.html#asyncio.ensure_future). Ручное создание экземпляров задач не рекомендуется.908909Для отмены выполняющейся задачи используйте метод [`cancel()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Task.cancel). Его вызов заставит задачу выбросить исключение [`CancelledError`](https://python-all.ru/3.15/library/asyncio-exceptions.html#asyncio.CancelledError) в обёрнутую корутину. Если во время отмены корутина ожидает объект, подобный future, ожидаемый объект будет отменён.910911[`cancelled()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Task.cancelled) можно использовать для проверки, была ли задача отменена. Метод возвращает `True`, если обёрнутая корутина не подавила исключение [`CancelledError`](https://python-all.ru/3.15/library/asyncio-exceptions.html#asyncio.CancelledError) и была действительно отменена.912913[`asyncio.Task`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Task) наследует от [`Future`](https://python-all.ru/3.15/library/asyncio-future.html#asyncio.Future) все свои API, за исключением [`Future.set_result()`](https://python-all.ru/3.15/library/asyncio-future.html#asyncio.Future.set_result) и [`Future.set_exception()`](https://python-all.ru/3.15/library/asyncio-future.html#asyncio.Future.set_exception).914915Необязательный именованный аргумент *context* позволяет задать пользовательский [`contextvars.Context`](https://python-all.ru/3.15/library/contextvars.html#contextvars.Context) для выполнения *coro*. Если *context* не указан, задача копирует текущий контекст и затем выполняет свою корутину в скопированном контексте.916917Необязательный именованный аргумент *eager\_start* позволяет начать выполнение [`asyncio.Task`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Task) немедленно при создании задачи. Если установлено в `True` и цикл событий работает, задача начнёт выполнять корутину сразу же до первого момента, когда корутина заблокируется. Если корутина возвращает результат или вызывает исключение без блокировки, задача завершится немедленно и не будет поставлена в очередь цикла событий.918919Задачи являются [обобщёнными](https://python-all.ru/3.15/library/typing.html#generics) по типу возвращаемого значения их обёрнутых корутин.920921Изменено в версии 3.7: Добавлена поддержка модуля [`contextvars`](https://python-all.ru/3.15/library/contextvars.html#module-contextvars).922923Изменено в версии 3.8: Добавлен параметр *name*.924925Устарело с версии 3.10: Предупреждение об устаревании выводится, если *loop* не указан и нет работающего цикла событий.926927Изменено в версии 3.11: Добавлен параметр *context*.928929Изменено в версии 3.12: Добавлен параметр *eager\_start*.930931#### `done()`932933Возвращает `True`, если задача *завершена*.934935Задача *завершена*, когда обёрнутая корутина вернула значение, возбудила исключение или задача была отменена.936937#### `result()`938939Возвращает результат задачи.940941Если задача *завершена*, возвращается результат обёрнутой корутины (или, если корутина возбудила исключение, это исключение возбуждается повторно).942943Если задача была *отменена*, этот метод возбуждает исключение [`CancelledError`](https://python-all.ru/3.15/library/asyncio-exceptions.html#asyncio.CancelledError).944945Если результат задачи ещё недоступен, этот метод возбуждает исключение [`InvalidStateError`](https://python-all.ru/3.15/library/asyncio-exceptions.html#asyncio.InvalidStateError).946947#### `exception()`948949Возвращает исключение задачи.950951Если обёрнутая корутина возбудила исключение, возвращается это исключение. Если корутина завершилась нормально, этот метод возвращает `None`.952953Если задача была *отменена*, этот метод возбуждает исключение [`CancelledError`](https://python-all.ru/3.15/library/asyncio-exceptions.html#asyncio.CancelledError).954955Если задача ещё не *завершена*, этот метод возбуждает исключение [`InvalidStateError`](https://python-all.ru/3.15/library/asyncio-exceptions.html#asyncio.InvalidStateError).956957#### `add_done_callback(callback, *, context=None)`958959Добавляет колбэк, который будет выполнен, когда задача *завершена*.960961Этот метод следует использовать только в низкоуровневом коде, основанном на колбэках.962963Подробнее см. в документации [`Future.add_done_callback()`](https://python-all.ru/3.15/library/asyncio-future.html#asyncio.Future.add_done_callback).964965#### `remove_done_callback(callback)`966967Удаляет *колбэк* из списка колбэков.968969Этот метод следует использовать только в низкоуровневом коде, основанном на колбэках.970971Подробнее см. в документации [`Future.remove_done_callback()`](https://python-all.ru/3.15/library/asyncio-future.html#asyncio.Future.remove_done_callback).972973#### `get_stack(*, limit=None)`974975Возвращает список кадров стека для этой задачи.976977Если обёрнутая корутина не завершена, возвращается стек, в котором она приостановлена. Если корутина успешно завершилась или была отменена, возвращается пустой список. Если корутина была завершена исключением, возвращается список кадров трассировки.978979Кадры всегда упорядочены от старых к новым.980981Для приостановленной корутины возвращается только один кадр стека.982983Необязательный аргумент *limit* задаёт максимальное количество возвращаемых кадров; по умолчанию возвращаются все доступные кадры. Порядок возвращаемого списка различается в зависимости от того, возвращается стек или трассировка: возвращаются новейшие кадры стека, но старейшие кадры трассировки. (Это соответствует поведению модуля traceback.)984985#### `print_stack(*, limit=None, file=None)`986987Печатает стек или трассировку для этой задачи.988989Результат аналогичен выводу модуля traceback для кадров, полученных с помощью [`get_stack()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Task.get_stack).990991Аргумент *limit* передаётся непосредственно в [`get_stack()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Task.get_stack).992993Аргумент *file* – это поток ввода-вывода, в который записывается вывод; по умолчанию вывод записывается в [`sys.stdout`](https://python-all.ru/3.15/library/sys.html#sys.stdout).994995#### `get_coro()`996997Возвращает объект корутины, обёрнутый `Task`.998999> **Примечание**1000>1001> Это вернёт `None` для задач, которые уже завершились нетерпеливо. См. [Eager Task Factory](https://python-all.ru/3.15/library/asyncio-task.html#eager-task-factory).10021003Добавлено в версии 3.8.10041005Изменено в версии 3.12: Добавлено нетерпеливое выполнение задач, поэтому результатом может быть `None`.10061007#### `get_context()`10081009Возвращает объект [`contextvars.Context`](https://python-all.ru/3.15/library/contextvars.html#contextvars.Context), связанный с задачей.10101011Добавлено в версии 3.12.10121013#### `get_name()`10141015Возвращает имя задачи.10161017Если задаче не было явно присвоено имя, стандартная реализация задач asyncio генерирует имя по умолчанию при создании.10181019Добавлено в версии 3.8.10201021#### `set_name(value)`10221023Устанавливает имя задачи.10241025Аргумент *value* может быть любым объектом, который затем преобразуется в строку.10261027В стандартной реализации задач имя будет видно в [`repr()`](https://python-all.ru/3.15/library/functions.html#repr)-выводе объекта задачи.10281029Добавлено в версии 3.8.10301031#### `cancel(msg=None)`10321033Запрашивает отмену задачи.10341035Если задача уже выполнена (*done*) или отменена (*cancelled*), возвращает `False`, в противном случае возвращает `True`.10361037Метод организует выброс исключения [`CancelledError`](https://python-all.ru/3.15/library/asyncio-exceptions.html#asyncio.CancelledError) в обёрнутую корутину на следующем цикле событий.10381039Затем корутина получает возможность выполнить очистку или даже отклонить запрос, подавив исключение с помощью блока [`try`](https://python-all.ru/3.15/reference/compound_stmts.html#try) … … `except CancelledError` … [`finally`](https://python-all.ru/3.15/reference/compound_stmts.html#finally). Поэтому, в отличие от [`Future.cancel()`](https://python-all.ru/3.15/library/asyncio-future.html#asyncio.Future.cancel), `Task.cancel()` не гарантирует, что задача будет отменена, хотя полное подавление отмены не является обычной практикой и активно не рекомендуется. Если корутина всё же решает подавить отмену, ей необходимо вызвать [`Task.uncancel()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Task.uncancel) в дополнение к перехвату исключения.10401041Если отменяемая задача в данный момент ожидает объект, подобный future, то этот ожидаемый объект также будет отменён. Отмена распространяется по всей цепочке ожидаемых объектов.10421043Изменено в версии 3.9: Добавлен параметр *msg*.10441045Изменено в версии 3.11: Параметр `msg` передаётся от отменённой задачи к ожидающему её объекту.10461047Следующий пример показывает, как корутины могут перехватить запрос отмены:10481049```python1050async def cancel_me():1051    print('cancel_me(): before sleep')10521053    try:1054        # Ждать 1 час1055        await asyncio.sleep(3600)1056    except asyncio.CancelledError:1057        print('cancel_me(): cancel sleep')1058        raise1059    finally:1060        print('cancel_me(): after sleep')10611062async def main():1063    # Создать задачу "cancel_me"1064    task = asyncio.create_task(cancel_me())10651066    # Ждать 1 секунду1067    await asyncio.sleep(1)10681069    task.cancel()1070    try:1071        await task1072    except asyncio.CancelledError:1073        print("main(): cancel_me is cancelled now")10741075asyncio.run(main())10761077# Ожидаемый вывод:1078#1079#     cancel_me(): перед сном1080#     cancel_me(): отмена сна1081#     cancel_me(): после сна1082#     main(): cancel_me теперь отменён1083```10841085#### `cancelled()`10861087Return `True` if the Task is *cancelled*.10881089The Task is *cancelled* when the cancellation was requested with [`cancel()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Task.cancel) and the wrapped coroutine propagated the [`CancelledError`](https://python-all.ru/3.15/library/asyncio-exceptions.html#asyncio.CancelledError) exception thrown into it.10901091#### `uncancel()`10921093Уменьшает счётчик запросов отмены для этой задачи.10941095Возвращает оставшееся количество запросов отмены.10961097Обратите внимание: после завершения выполнения отменённой задачи последующие вызовы `uncancel()` неэффективны.10981099Добавлено в версии 3.12.11001101Этот метод используется внутренними механизмами asyncio и не предназначен для использования в пользовательском коде. В частности, если задача успешно отменяет отмену, это позволяет элементам структурированной параллельности, таким как [группы задач](https://python-all.ru/3.15/library/asyncio-task.html#taskgroups) и [`asyncio.timeout()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.timeout), продолжить выполнение, изолируя отмену в соответствующем структурированном блоке. Например:11021103```python1104async def make_request_with_timeout():1105    try:1106        async with asyncio.timeout(1):1107            # Структурированный блок, на который влияет таймаут:1108            await make_request()1109            await make_another_request()1110    except TimeoutError:1111        log("There was a timeout")1112    # Внешний код, на который таймаут не влияет:1113    await unrelated_code()1114```11151116Хотя блок с `make_request()` и `make_another_request()` может быть отменён из-за тайм-аута, `unrelated_code()` должен продолжить работу даже в случае тайм-аута. Это реализовано с помощью `uncancel()`. Менеджеры контекста [`TaskGroup`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.TaskGroup) используют [`uncancel()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Task.uncancel) аналогичным образом.11171118Если пользовательский код по какой-либо причине подавляет отмену, перехватывая [`CancelledError`](https://python-all.ru/3.15/library/asyncio-exceptions.html#asyncio.CancelledError), ему необходимо вызвать этот метод, чтобы удалить состояние отмены.11191120Когда этот метод уменьшает счётчик отмены до нуля, он проверяет, планировал ли предыдущий вызов [`cancel()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Task.cancel) выброс [`CancelledError`](https://python-all.ru/3.15/library/asyncio-exceptions.html#asyncio.CancelledError) в задачу. Если он ещё не был выброшен, это планирование отменяется (сбросом внутреннего флага `_must_cancel`).11211122Изменено в версии 3.13: Изменено для отмены ожидающих запросов отмены при достижении нуля.11231124#### `cancelling()`11251126Возвращает количество ожидающих запросов отмены для этой задачи, т.е. количество вызовов [`cancel()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Task.cancel) минус количество вызовов [`uncancel()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Task.uncancel).11271128Обратите внимание: если это число больше нуля, но задача всё ещё выполняется, [`cancelled()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Task.cancelled) всё равно вернёт `False`. Это связано с тем, что это число можно уменьшить вызовом [`uncancel()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Task.uncancel), что может привести к тому, что задача в итоге не будет отменена, если количество запросов отмены снизится до нуля.11291130Этот метод используется во внутренних механизмах asyncio и не предназначен для использования в пользовательском коде. Подробнее см. [`uncancel()`](https://python-all.ru/3.15/library/asyncio-task.html#asyncio.Task.uncancel).11311132Добавлено в версии 3.12.1133