Сорутины и задачи
В этом разделе описаны основные API asyncio для работы с сорутинами и задачами.
- Сорутины
- Awaitables
- Создание задач
- Отмена задач
- Группы задач
- Ожидание
- Выполнение задач одновременно
- Жадный фабрикант задач
- Защита от отмены
- Таймауты
- Примитивы ожидания
- Выполнение в потоках
- Планирование из других потоков
- Интроспекция
- Объект задачи
Сорутины
Исходный код: Lib/asyncio/coroutines.py
Сорутины, объявленные с помощью синтаксиса async/await, — это предпочтительный способ написания приложений asyncio. Например, следующий фрагмент кода выводит «hello», ждёт 1 секунду, а затем выводит «world»:
>>> import asyncio
>>> async def main():
... print('hello')
... await asyncio.sleep(1)
... print('world')
>>> asyncio.run(main())
hello
world
Обратите внимание, что просто вызов сорутины не запланирует её выполнение:
>>> main() <coroutine object main at 0x1053bb7c8>
Для фактического запуска сорутины asyncio предоставляет следующие механизмы:
- Функция
asyncio.run()для запуска функции «main()» (см. пример выше). -
Ожидание сорутины. Следующий фрагмент кода выведет «hello» после ожидания 1 секунды, а затем выведет «world» после ожидания ещё 2 секунд:
import asyncio import time async def say_after(delay, what): await asyncio.sleep(delay) print(what) async def main(): print(f"started at {time.strftime('%X')}") await say_after(1, 'hello') await say_after(2, 'world') print(f"finished at {time.strftime('%X')}") asyncio.run(main())Ожидаемый вывод:
started at 17:13:52 hello world finished at 17:13:55
-
Функция
asyncio.create_task()для одновременного выполнения сорутин как задач asyncioTasks.Давайте изменим пример выше и запустим две
say_afterсорутины одновременно:async def main(): task1 = asyncio.create_task( say_after(1, 'hello')) task2 = asyncio.create_task( say_after(2, 'world')) print(f"started at {time.strftime('%X')}") # Wait until both tasks are completed (should take # around 2 seconds.) await task1 await task2 print(f"finished at {time.strftime('%X')}")Обратите внимание, что ожидаемый вывод теперь показывает, что фрагмент выполняется на 1 секунду быстрее, чем раньше:
started at 17:14:32 hello world finished at 17:14:34
-
Класс
asyncio.TaskGroupпредоставляет более современную альтернативуcreate_task(). Используя этот API, последний пример становится:async def main(): async with asyncio.TaskGroup() as tg: task1 = tg.create_task( say_after(1, 'hello')) task2 = tg.create_task( say_after(2, 'world')) print(f"started at {time.strftime('%X')}") # The await is implicit when the context manager exits. print(f"finished at {time.strftime('%X')}")Временные характеристики и вывод должны быть такими же, как и в предыдущей версии.
Добавлена в версии 3.11:
asyncio.TaskGroup.
Awaitables
Мы говорим, что объект является awaitable объектом, если он может использоваться в выражении await. Многие API asyncio предназначены для приема awaitables.
Существует три основных типа awaitable объектов: сорутины, задачи и будущие.
Сорутины
Python сорутины являются awaitable и поэтому могут ожидать другие сорутины:
import asyncio
async def nested():
return 42
async def main():
# Nothing happens if we just call "nested()".
# A coroutine object is created but not awaited,
# so it *won't run at all*.
nested()
# Let's do it differently now and await it:
print(await nested()) # will print "42".
asyncio.run(main())
Важно
В этом руководстве термин «сорутина» может использоваться для двух тесно связанных понятий:
- функция сорутины: функция
async def; - объект сорутины: объект, возвращаемый при вызове функции сорутины.
Задачи
Задачи используются для планирования сорутин одновременно.
Когда сорутина оборачивается в задачу с помощью функций, таких как asyncio.create_task(), сорутина автоматически планируется на ближайшее время выполнения:
import asyncio
async def nested():
return 42
async def main():
# Schedule nested() to run soon concurrently
# with "main()".
task = asyncio.create_task(nested())
# "task" can now be used to cancel "nested()", or
# can simply be awaited to wait until it is complete:
await task
asyncio.run(main())
Будущие
Объект Future — это специальный низкоуровневый awaitable объект, представляющий конечный результат асинхронной операции.
Когда объект Future ожидается, это означает, что сорутина будет ждать, пока Future не будет разрешен в другом месте.
Объекты Future в asyncio необходимы для использования кода на основе обратного вызова с async/await.
Обычно нет необходимости создавать объекты Future в коде приложения.
Объекты Future, иногда предоставляемые библиотеками и некоторыми API asyncio, могут ожидать:
async def main():
await function_that_returns_a_future_object()
# this is also valid:
await asyncio.gather(
function_that_returns_a_future_object(),
some_python_coroutine()
)
Хороший пример низкоуровневой функции, возвращающей объект Future, — loop.run_in_executor().
Создание задач
Исходный код: Lib/asyncio/tasks.py
-
asyncio.create_task(coro, *, name=None, context=None) -
Оборачивает coro сорутину в
Taskи планирует её выполнение. Возвращает объект Task.Если name не
None, он устанавливается как имя задачи с помощьюTask.set_name().Необязательный ключевой аргумент context позволяет указать пользовательский
contextvars.Contextдля выполнения coro. Копия текущего контекста создаётся, если context не указан.Задача выполняется в цикле, возвращённом
get_running_loop(),RuntimeErrorгенерируется, если в текущем потоке нет работающего цикла.Примечание
asyncio.TaskGroup.create_task()— это новая альтернатива, использующая структурную конкуретность; она позволяет ожидать группу связанных задач с сильными гарантиями безопасности.Важно
Сохраните ссылку на результат этой функции, чтобы избежать исчезновения задачи во время выполнения. Цикл событий хранит только слабые ссылки на задачи. Задача, на которую нет других ссылок, может быть удалена сборщиком мусора в любой момент, даже до завершения. Для надёжных «забытых» фоновых задач соберите их в коллекции:
background_tasks = set() for i in range(10): task = asyncio.create_task(some_coro(param=i)) # Add task to the set. This creates a strong reference. background_tasks.add(task) # To prevent keeping references to finished tasks forever, # make each task remove its own reference from the set after # completion: task.add_done_callback(background_tasks.discard)Добавлена в версии 3.7.
Изменена в версии 3.8: Добавлен параметр name.
Изменена в версии 3.11: Добавлен параметр context.
Отмена задач
Задачи легко и безопасно отменяются. При отмене задачи asyncio.CancelledError будет возбуждена в задаче в следующий удобный момент.
Рекомендуется, чтобы сорутины использовали блоки try/finally для надёжной реализации логики очистки. В случае, если asyncio.CancelledError явно перехватывается, его следует распространять после завершения очистки. asyncio.CancelledError напрямую наследуется от BaseException, поэтому большинство кода не должны быть осведомлены об этом.
Компоненты asyncio, поддерживающие структурированную конкуретность, такие как asyncio.TaskGroup и asyncio.timeout(), реализованы с использованием внутренней отмены и могут работать некорректно, если сорутина подавляет asyncio.CancelledError. Аналогично, пользовательский код обычно не должен вызывать uncancel. Однако, в случаях, когда подавление asyncio.CancelledError действительно необходимо, необходимо также вызвать uncancel() для полного удаления состояния отмены.
Группы задач
Группы задач объединяют API создания задач с удобным и надежным способом ожидания завершения всех задач в группе.
-
class asyncio.TaskGroup -
Управляемый контекст асинхронного менеджера контекста содержащий группу задач. Задачи могут быть добавлены в группу с помощью
create_task(). Все задачи ожидают завершения при выходе из менеджера контекста.Добавлен в версии 3.11.
-
create_task(coro, *, name=None, context=None) -
Создает задачу в этой группе задач. Подпись соответствует подписи
asyncio.create_task().
-
Пример:
async def main():
async with asyncio.TaskGroup() as tg:
task1 = tg.create_task(some_coro(...))
task2 = tg.create_task(another_coro(...))
print(f"Both tasks have completed now: {task1.result()}, {task2.result()}")
Инструкция async with будет ожидать завершения всех задач в группе. В процессе ожидания новые задачи могут быть добавлены в группу (например, передав tg в одну из корутин и вызвав tg.create_task() в этой корутине). После завершения последней задачи и выхода из блока async with, новые задачи не могут быть добавлены в группу.
В первый раз, когда любая из задач в группе завершится с исключением, отличным от asyncio.CancelledError, оставшиеся задачи в группе отменяются. После этого больше задач в группу добавить нельзя. В этот момент, если тело инструкции async with всё ещё активно (т.е. __aexit__() ещё не было вызвано), сама задача, содержащая инструкцию async with также отменяется. Результатом будет asyncio.CancelledError, который прервёт await, но он не будет распространяться за пределы содержащей async with инструкции.
После завершения всех задач, если какие-либо задачи завершились с исключением, отличным от asyncio.CancelledError, эти исключения объединяются в ExceptionGroup или BaseExceptionGroup (в зависимости от ситуации; см. их документацию), которое затем и генерируется.
Два базовых исключения обрабатываются особым образом: если какая-либо задача завершается с KeyboardInterrupt или SystemExit, группа задач всё равно отменяет оставшиеся задачи и ожидает их завершения, но затем исходное KeyboardInterrupt или SystemExit переиздаётся вместо ExceptionGroup или BaseExceptionGroup.
Если тело инструкции async with завершается с исключением (т.е. вызывается __aexit__() с установленным исключением), это обрабатывается так же, как если бы одна из задач завершилась с исключением: оставшиеся задачи отменяются, и затем ожидают завершения. Исключение, не являющееся исключением отмены, объединяется в группу исключений и генерируется. Исключение, переданное в __aexit__(), если оно не является asyncio.CancelledError, также включается в группу исключений. Для KeyboardInterrupt и SystemExit применяется тот же специальный случай, что и в предыдущем абзаце.
Ожидание
-
coroutine asyncio.sleep(delay, result=None) -
Ожидание в течение delay секунд.
Если result указан, он возвращается вызывающей стороне при завершении корутины.
sleep()всегда приостанавливает текущую задачу, позволяя другим задачам выполняться.Установка задержки в 0 предоставляет оптимизированный путь, позволяющий другим задачам выполняться. Это может быть использовано функциями с длительным выполнением, чтобы избежать блокировки цикла событий на всё время выполнения функции.
Пример корутины, отображающей текущую дату каждую секунду в течение 5 секунд:
import asyncio import datetime async def display_date(): loop = asyncio.get_running_loop() end_time = loop.time() + 5.0 while True: print(datetime.datetime.now()) if (loop.time() + 1.0) >= end_time: break await asyncio.sleep(1) asyncio.run(display_date())Изменено в версии 3.10: Убран параметр loop.
Запуск задач параллельно
-
awaitable asyncio.gather(*aws, return_exceptions=False) -
Запуск объектов-ожиданий в последовательности aws параллельно.
Если любой объект-ожидание в aws является корутиной, он автоматически планируется как задача.
Если все объекты-ожидания завершились успешно, результатом является агрегированный список возвращаемых значений. Порядок значений результата соответствует порядку объектов-ожиданий в aws.
Если return_exceptions
False(по умолчанию), первое сгенерированное исключение немедленно передаётся задаче, которая ожидаетgather(). Другие объекты-ожидания в последовательности aws не будут отменены и будут продолжать выполняться.Если return_exceptions
True, исключения обрабатываются так же, как успешные результаты, и агрегируются в списке результатов.Если
gather()отменён, все отправленные объекты-ожидания (которые ещё не завершены) также отменяются.Если какая-либо задача или будущий объект из последовательности aws отменён, это обрабатывается так, как будто было сгенерировано
CancelledError— вызовgather()в этом случае не отменяется. Это предотвращает отмену одной отправленной задачи/будущего объекта, которая могла бы вызвать отмену других задач/будущих объектов.Примечание
Альтернативным способом создания и запуска задач параллельно и ожидания их завершения является
asyncio.TaskGroup. TaskGroup обеспечивает более сильные гарантии безопасности по сравнению с gather для планирования вложенных подзадач: если задача (или подзадача, задача, запланированная другой задачей) генерирует исключение, TaskGroup, в отличие от gather, отменит оставшиеся запланированные задачи.Пример:
import asyncio async def factorial(name, number): f = 1 for i in range(2, number + 1): print(f"Task {name}: Compute factorial({number}), currently i={i}...") await asyncio.sleep(1) f *= i print(f"Task {name}: factorial({number}) = {f}") return f async def main(): # Schedule three calls *concurrently*: L = await asyncio.gather( factorial("A", 2), factorial("B", 3), factorial("C", 4), ) print(L) asyncio.run(main()) # Expected output: # # Task A: Compute factorial(2), currently i=2... # Task B: Compute factorial(3), currently i=2... # Task C: Compute factorial(4), currently i=2... # Task A: factorial(2) = 2 # Task B: Compute factorial(3), currently i=3... # Task C: Compute factorial(4), currently i=3... # Task B: factorial(3) = 6 # Task C: Compute factorial(4), currently i=4... # Task C: factorial(4) = 24 # [2, 6, 24]Примечание
Если return_exceptions ложно, отмена gather() после того, как он был помечен как выполненный, не отменяет никаких отправленных объектов-ожиданий. Например, gather может быть помечен как выполненный после распространения исключения вызывающей стороне, поэтому вызов
gather.cancel()после перехвата исключения (сгенерированного одним из объектов-ожиданий) от gather не отменит другие объекты-ожидания.Изменено в версии 3.7: Если сам gather отменён, отмена распространяется независимо от return_exceptions.
Изменено в версии 3.10: Убран параметр loop.
Устарело начиная с версии 3.10: Если не указаны позиционные аргументы или не все позиционные аргументы являются объектами типа Future, и нет активного цикла событий, выводится предупреждение об устаревании.
Фабрика задач с немедленным выполнением
-
asyncio.eager_task_factory(loop, coro, *, name=None, context=None) -
Фабрика задач для немедленного выполнения.
При использовании этой фабрики (через
loop.set_task_factory(asyncio.eager_task_factory)), корутины начинают выполняться синхронно во время созданияTask. Задачи планируются в цикле событий только в случае блокировки. Это может улучшить производительность, так как исключается накладные расходы на планирование цикла событий для корутин, которые завершаются синхронно.Частый пример, где это полезно, — корутины, использующие кэширование или запоминание, чтобы избежать реального ввода-вывода, когда это возможно.
Примечание
Немедленное выполнение корутины — это изменение семантики. Если корутина возвращает значение или вызывает исключение, задача никогда не планируется в цикле событий. Если выполнение корутины блокируется, задача планируется в цикле событий. Это изменение может привести к изменениям поведения в существующих приложениях. Например, порядок выполнения задач в приложении, скорее всего, изменится.
Добавлена в версии 3.12.
-
asyncio.create_eager_task_factory(custom_task_constructor) -
Создает фабрику задач с немедленным выполнением, аналогично
eager_task_factory(), используя предоставленный custom_task_constructor при создании новой задачи вместо стандартногоTask.custom_task_constructor должен быть вызываемым объектом со сигнатурой, соответствующей сигнатуре
Task.__init__. Вызываемый объект должен возвращать объект, совместимый сasyncio.Task.Эта функция возвращает вызываемый объект, предназначенный для использования в качестве фабрики задач цикла событий через
loop.set_task_factory(factory).Добавлена в версии 3.12.
Защита от отмены
-
awaitable asyncio.shield(aw) -
Защищает объект-выражение-ожидания от отмены
cancelled.Если aw является корутиной, она автоматически планируется как задача.
Выражение:
task = asyncio.create_task(something()) res = await shield(task)
эквивалентно:
res = await something()
за исключением того, что если корутина, содержащая его, отменяется, задача, выполняющаяся в
something()не отменяется. С точки зренияsomething(), отмена не произошла. Хотя вызывающий её код всё равно отменён, поэтому выражение «await» по-прежнему вызываетCancelledError.Если
something()отменяется другими способами (т.е. изнутри), это также отменяетshield().Если нужно полностью игнорировать отмену (не рекомендуется), функция
shield()должна использоваться совместно с блоком try/except, как показано ниже:task = asyncio.create_task(something()) try: res = await shield(task) except CancelledError: res = NoneВажно
Сохраняйте ссылки на задачи, переданные в эту функцию, чтобы избежать исчезновения задачи во время выполнения. Цикл событий сохраняет только слабые ссылки на задачи. Задача, на которую нет других ссылок, может быть удалена сборщиком мусора в любое время, даже прежде чем она завершится.
Изменено в версии 3.10: Параметр loop удален.
Устарело начиная с версии 3.10: Выводится предупреждение об устаревании, если aw не является объектом типа Future и нет работающего цикла событий.
Таймауты
-
asyncio.timeout(delay) -
Возвращает асинхронный контекстный менеджер, который можно использовать для ограничения времени ожидания чего-либо.
delay может быть
None, или числом/вещественным числом секунд для ожидания. Если delayNone, ограничение по времени не будет применено; это может быть полезно, если задержка неизвестна при создании контекстного менеджера.В любом случае, контекстный менеджер можно перепланировать после создания с помощью
Timeout.reschedule().Пример:
async def main(): async with asyncio.timeout(10): await long_running_task()Если
long_running_taskзаймёт более 10 секунд, контекстный менеджер отменит текущую задачу и обработает возникшееasyncio.CancelledErrorвнутри, преобразуя его вTimeoutError, которое можно перехватить и обработать.Примечание
Контекстный менеджер
asyncio.timeout()преобразуетasyncio.CancelledErrorвTimeoutError, что означает, чтоTimeoutErrorможет быть перехвачен вне контекстного менеджера.Пример перехвата
TimeoutError:async def main(): try: async with asyncio.timeout(10): await long_running_task() except TimeoutError: print("The long operation timed out, but we've handled it.") print("This statement will run regardless.")Контекстный менеджер, созданный функцией
asyncio.timeout(), может быть перепланирован на другую дату истечения срока действия и проинспектирован.-
class asyncio.Timeout(when) -
Асинхронный контекстный менеджер для отмены просроченных корутин.
whenдолжно быть абсолютным временем истечения срока действия контекста, измеренным по часам цикла событий:- Если
whenравноNone, таймаут никогда не сработает. - Если
when < loop.time(), таймаут сработает на следующей итерации цикла событий.
-
when() → float | None -
Возвращает текущую дату истечения срока действия или
None, если текущая дата истечения срока действия не установлена.
-
reschedule(when: float | None) -
Перепланировать таймаут.
-
expired() → bool -
Возвращает значение, указывающее, превышен ли контекстный менеджер заданный срок действия (истек).
- Если
Пример:
async def main(): try: # We do not know the timeout when starting, so we pass ``None``. async with asyncio.timeout(None) as cm: # We know the timeout now, so we reschedule it. new_deadline = get_running_loop().time() + 10 cm.reschedule(new_deadline) await long_running_task() except TimeoutError: pass if cm.expired(): print("Looks like we haven't finished on time.")Контекстные менеджеры таймаута могут быть вложены безопасно.
Добавлена в версии 3.11.
-
-
asyncio.timeout_at(when) -
Аналогично
asyncio.timeout(), за исключением того, что when — это абсолютное время остановки ожидания илиNone.Пример:
async def main(): loop = get_running_loop() deadline = loop.time() + 20 try: async with asyncio.timeout_at(deadline): await long_running_task() except TimeoutError: print("The long operation timed out, but we've handled it.") print("This statement will run regardless.")Добавлена в версии 3.11.
-
coroutine asyncio.wait_for(aw, timeout) -
Ожидать завершения выражения ожидания aw с таймаутом.
Если aw является корутиной, она автоматически планируется как задача.
timeout может быть
Noneили числом (float или int) секунд для ожидания. Если timeoutNone, ждать завершения будущего.В случае таймаута, задача отменяется и генерируется
TimeoutError.Чтобы избежать отмены задачи
cancellation, оберните её вshield().Функция будет ждать, пока будущее не будет фактически отменено, поэтому общее время ожидания может превысить timeout. Если во время отмены произойдет исключение, оно будет распространено.
Если ожидание отменено, будущее aw также отменяется.
Пример:
async def eternity(): # Sleep for one hour await asyncio.sleep(3600) print('yay!') async def main(): # Wait for at most 1 second try: await asyncio.wait_for(eternity(), timeout=1.0) except TimeoutError: print('timeout!') asyncio.run(main()) # Expected output: # # timeout!Изменено в версии 3.7: Когда aw отменяется из-за таймаута,
wait_forждёт отмены aw. Раньше оно сразу генерировалоTimeoutError.Изменено в версии 3.10: Параметр loop удалён.
Изменено в версии 3.11: Возвращает
TimeoutErrorвместоasyncio.TimeoutError.
Операции ожидания
-
coroutine asyncio.wait(aws, *, timeout=None, return_when=ALL_COMPLETED) -
Выполните экземпляры
FutureиTaskв итерируемом объекте aws одновременно и заблокируйтесь до тех пор, пока не будет выполнено условие, заданное параметром return_when.Итерируемый объект aws не должен быть пустым.
Возвращает два набора задач/будущих:
(done, pending).Пример использования:
done, pending = await asyncio.wait(aws)
timeout (вещественное или целое число), если указан, может быть использован для управления максимальным временем ожидания в секундах, после чего произойдет возврат.
Обратите внимание, что эта функция не вызывает
TimeoutError. Будущие или задачи, которые не завершены к моменту наступления таймаута, просто возвращаются во второй набор.return_when указывает, когда функция должна вернуть результат. Оно должно быть одним из следующих констант:
Константа
Описание
-
asyncio.FIRST_COMPLETED
Функция вернётся, когда любая будущая задача завершится или будет отменена.
-
asyncio.FIRST_EXCEPTION
Функция вернётся, когда любая будущая задача завершится с исключением. Если ни одна будущая задача не вызывает исключения, то это эквивалентно
ALL_COMPLETED.-
asyncio.ALL_COMPLETED
Функция вернётся, когда все будущие задачи завершатся или будут отменены.
В отличие от
wait_for(),wait()не отменяет будущие задачи при наступлении таймаута.Изменено в версии 3.10: Устранён параметр loop.
Изменено в версии 3.11: Передача объектов корутин в
wait()напрямую запрещена.Изменено в версии 3.12: Добавлена поддержка генераторов, возвращающих задачи.
-
-
asyncio.as_completed(aws, *, timeout=None) -
Выполните awaitable объекты в итерируемом объекте aws одновременно. Возвращает итератор корутин. Каждая возвращённая корутина может быть ожидаема для получения самого раннего следующего результата из итерируемого объекта оставшихся awaitables.
Вызывает
TimeoutError, если таймаут наступает до того, как все будущие задачи завершатся.Пример:
for coro in as_completed(aws): earliest_result = await coro # ...Изменено в версии 3.10: Устранён параметр loop.
Устарело начиная с версии 3.10: Выводится предупреждение об устаревании, если все awaitable объекты в итерируемом объекте aws не являются объектами типа Future, и нет работающего цикла событий.
Изменено в версии 3.12: Добавлена поддержка генераторов, возвращающих задачи.
Запуск в потоках
-
coroutine asyncio.to_thread(func, /, *args, **kwargs) -
Асинхронно выполняет функцию func в отдельном потоке.
Любые *args и **kwargs, переданные в эту функцию, напрямую передаются в func. Также, текущий
contextvars.Contextраспространяется, позволяя обращаться к переменным контекста из потока цикла событий в отдельном потоке.Возвращает корутину, которую можно ожидать, чтобы получить конечный результат func.
Эта функция корутин предназначена в первую очередь для выполнения функций/методов, связанных с вводом-выводом, которые в противном случае блокировали бы цикл событий, если бы они выполнялись в главном потоке. Например:
def blocking_io(): print(f"start blocking_io at {time.strftime('%X')}") # Note that time.sleep() can be replaced with any blocking # IO-bound operation, such as file operations. time.sleep(1) print(f"blocking_io complete at {time.strftime('%X')}") async def main(): print(f"started main at {time.strftime('%X')}") await asyncio.gather( asyncio.to_thread(blocking_io), asyncio.sleep(1)) print(f"finished main at {time.strftime('%X')}") asyncio.run(main()) # Expected output: # # started main at 19:50:53 # start blocking_io at 19:50:53 # blocking_io complete at 19:50:54 # finished main at 19:50:54Прямое вызов
blocking_io()в любой корутине заблокирует цикл событий на всё время её выполнения, что приведёт к дополнительному 1 секунде времени выполнения. Вместо этого, используяasyncio.to_thread(), мы можем выполнить его в отдельном потоке, не блокируя цикл событий.Примечание
Из-за GIL,
asyncio.to_thread()обычно можно использовать только для того, чтобы сделать функции, связанные с вводом-выводом, неблокирующими. Однако, для модулей расширения, которые освобождают GIL или альтернативных реализаций Python, которые его не имеют,asyncio.to_thread()также можно использовать для функций, связанных с вычислениями.Добавлена в версии 3.9.
Планирование из других потоков
-
asyncio.run_coroutine_threadsafe(coro, loop) -
Отправляет корутину в заданный цикл событий. Потокобезопасная.
Возвращает
concurrent.futures.Futureдля ожидания результата из другого потока ОС.Эта функция предназначена для вызова из другого потока ОС, отличного от того, в котором работает цикл событий. Пример:
# Create a coroutine coro = asyncio.sleep(1, result=3) # Submit the coroutine to a given loop future = asyncio.run_coroutine_threadsafe(coro, loop) # Wait for the result with an optional timeout argument assert future.result(timeout) == 3
Если в корутине возникает исключение, возвращаемая будущая задача будет уведомлена. Она также может быть использована для отмены задачи в цикле событий:
try: result = future.result(timeout) except TimeoutError: print('The coroutine took too long, cancelling the task...') future.cancel() except Exception as exc: print(f'The coroutine raised an exception: {exc!r}') else: print(f'The coroutine returned: {result!r}')См. раздел совмещенность и многопоточность документации.
В отличие от других функций asyncio, эта функция требует явного указания аргумента loop.
Добавлена в версии 3.5.1.
Интроспекция
-
asyncio.current_task(loop=None) -
Возвращает текущий работающий экземпляр
Task, илиNoneесли нет работающей задачи.Если loop не указан, используется
get_running_loop()для получения текущего цикла.Добавлена в версии 3.7.
-
asyncio.all_tasks(loop=None) -
Возвращает множество ещё не завершённых объектов
Task, запущенных циклом.Если loop не указан, используется
get_running_loop()для получения текущего цикла.Добавлена в версии 3.7.
-
asyncio.iscoroutine(obj) -
Возвращает
Trueесли obj является объектом корутины.Добавлена в версии 3.4.
Объект задачи
-
class asyncio.Task(coro, *, loop=None, name=None, context=None, eager_start=False) -
Объект
Future-like, выполняющий Python-корутину. Не потокобезопасен.Задачи используются для запуска корутин в циклах событий. Если корутина ожидает Future, задача приостанавливает выполнение корутины и ждёт завершения Future. Когда Future завершается, возобновляется выполнение обернутой корутины.
Циклы событий используют кооперативное планирование: цикл событий выполняет одну задачу за раз. Пока задача ожидает завершения Future, цикл событий выполняет другие задачи, обратные вызовы или выполняет операции ввода-вывода.
Используйте высокоуровневую функцию
asyncio.create_task()для создания задач или низкоуровневые функцииloop.create_task()илиensure_future(). Ручное создание задач не рекомендуется.Для отмены выполняемой задачи используйте метод
cancel(). Это вызовет исключениеCancelledErrorв обернутой корутине. Если во время отмены корутина ожидает объект Future, объект Future будет отменён.Можно проверить, была ли задача отменена с помощью
cancelled(). Метод возвращаетTrue, если обернутая корутина не подавила исключениеCancelledErrorи была фактически отменена.asyncio.Taskнаследует все свои API отFuture, за исключениемFuture.set_result()иFuture.set_exception().Необязательный ключевой аргумент context позволяет указать пользовательский
contextvars.Contextдля выполнения coro. Если context не указан, задача копирует текущий контекст и позже выполняет свою корутину в скопированном контексте.Необязательный ключевой аргумент eager_start позволяет начать выполнение
asyncio.Taskпри создании задачи. Если установленоTrue, и цикл событий запущен, задача начнёт немедленно выполнять корутину, до первого момента блокировки корутины. Если корутина возвращается или вызывает исключение без блокировки, задача завершится сразу и пропустит планирование в цикле событий.Изменено в версии 3.7: Добавлена поддержка модуля
contextvars.Изменено в версии 3.8: Добавлен параметр name.
Устарело начиная с версии 3.10: Выдаётся предупреждение об устаревании, если loop не указан и нет работающего цикла событий.
Изменено в версии 3.11: Добавлен параметр context.
Изменено в версии 3.12: Добавлен параметр eager_start.
-
done() -
Возвращает
True, если задача завершена.Задача завершается, когда обернутая корутина вернула значение, вызвала исключение или задача была отменена.
-
result() -
Возвращает результат выполнения задачи.
Если задача завершена, возвращается результат выполнения обернутой корутины (или, если корутина вызвала исключение, то это исключение перевыдаётся).
Если задача была отменена, этот метод вызывает исключение
CancelledError.Если результат задачи ещё недоступен, этот метод вызывает исключение
InvalidStateError.
-
exception() -
Возвращает исключение задачи.
Если обернутая корутина вызвала исключение, возвращается это исключение. Если обернутая корутина завершилась успешно, этот метод возвращает
None.Если задача была отменена, этот метод вызывает исключение
CancelledError.Если задача ещё не завершена, этот метод вызывает исключение
InvalidStateError.
-
add_done_callback(callback, *, context=None) -
Добавляет обратный вызов, который будет выполнен, когда задача завершится.
Этот метод следует использовать только в низкоуровневом коде, основанном на обратных вызовах.
См. документацию
Future.add_done_callback()для получения более подробной информации.
-
remove_done_callback(callback) -
Удаляет callback из списка обратных вызовов.
Этот метод следует использовать только в низкоуровневом коде, основанном на обратных вызовах.
См. документацию
Future.remove_done_callback()для получения более подробной информации.
-
get_stack(*, limit=None) -
Возвращает список кадров стека для этой задачи.
Если обернутая корутина ещё не завершена, возвращается стек, где она приостановлена. Если корутина завершилась успешно или была отменена, возвращается пустой список. Если корутина была завершена исключением, возвращается список кадров трассировки.
Кадры всегда упорядочены от старых к новым.
Для приостановленной корутины возвращается только один кадр стека.
Необязательный аргумент limit задаёт максимальное количество возвращаемых кадров; по умолчанию возвращаются все доступные кадры. Порядок возвращаемого списка отличается в зависимости от того, возвращается ли стек или трассировка: возвращаются самые новые кадры стека, но самые старые кадры трассировки. (Это соответствует поведению модуля traceback).
-
print_stack(*, limit=None, file=None) -
Выводит стек или трассировку для этой задачи.
Это генерирует вывод, похожий на вывод модуля traceback для кадров, полученных с помощью
get_stack().Аргумент limit передаётся в
get_stack()напрямую.Аргумент file — это поток ввода-вывода, в который записывается вывод; по умолчанию вывод записывается в
sys.stdout.
-
get_coro() -
Возвращает объект корутины, обернутый в
Task.Примечание
Для задач, которые уже завершились быстро, это вернёт
None. См. Eager Task Factory.Добавлена в версии 3.8.
Изменено в версии 3.12: Новое добавление быстрого выполнения задачи означает, что результат может быть
None.
-
get_context() -
Возвращает объект
contextvars.Context, связанный с задачей.Добавлена в версии 3.12.
-
get_name() -
Возвращает имя задачи.
Если задаче явно не было присвоено имя, реализация asyncio Task по умолчанию генерирует имя по умолчанию во время создания.
Добавлена в версии 3.8.
-
set_name(value) -
Устанавливает имя задачи.
Аргумент value может быть любым объектом, который затем преобразуется в строку.
В реализации задачи по умолчанию имя будет видно в выводе
repr()объекта задачи.Добавлена в версии 3.8.
-
-
cancel(msg=None) -
Запросить отмену задачи.
Это организует бросок исключения
CancelledErrorв оборачиваемую корутину в следующем цикле событий.Корутина получает возможность выполнить очистку или даже отклонить запрос, подавив исключение с помощью блока
try… …except CancelledError…finally. Поэтому, в отличие отFuture.cancel(),Task.cancel()не гарантирует отмену задачи, хотя полное подавление отмены не является распространенной практикой и активно не рекомендуется. Если корутина все же решит подавить отмену, ей необходимо вызватьTask.uncancel()в дополнение к перехвату исключения.Изменено в версии 3.9: Добавлен параметр msg.
Изменено в версии 3.11: Параметр
msgпередается от отменённой задачи к её ожидателю.Следующий пример иллюстрирует, как корутины могут перехватывать запрос на отмену:
async def cancel_me(): print('cancel_me(): before sleep') try: # Wait for 1 hour await asyncio.sleep(3600) except asyncio.CancelledError: print('cancel_me(): cancel sleep') raise finally: print('cancel_me(): after sleep') async def main(): # Create a "cancel_me" Task task = asyncio.create_task(cancel_me()) # Wait for 1 second await asyncio.sleep(1) task.cancel() try: await task except asyncio.CancelledError: print("main(): cancel_me is cancelled now") asyncio.run(main()) # Expected output: # # cancel_me(): before sleep # cancel_me(): cancel sleep # cancel_me(): after sleep # main(): cancel_me is cancelled now
-
cancelled() -
Возвращает
True, если задача отменена.Задача отменена, когда запрос на отмену был сделан с помощью
cancel()и оборачиваемая корутина распространила исключениеCancelledError, сгенерированное в ней.
-
uncancel() -
Уменьшает счётчик запросов на отмену для этой задачи.
Возвращает оставшееся количество запросов на отмену.
Обратите внимание, что после завершения выполнения отменённой задачи дальнейшие вызовы
uncancel()неэффективны.Добавлена в версии 3.11.
Этот метод используется внутренними механизмами asyncio и не должен использоваться кодом конечного пользователя. В частности, если задача успешно отменена, это позволяет элементам структурированной конкурентности, таким как Группы задач и
asyncio.timeout(), продолжить работу, изолировав отмену для соответствующего структурированного блока. Например:async def make_request_with_timeout(): try: async with asyncio.timeout(1): # Structured block affected by the timeout: await make_request() await make_another_request() except TimeoutError: log("There was a timeout") # Outer code not affected by the timeout: await unrelated_code()Хотя блок с
make_request()иmake_another_request()может быть отменён из-за таймаута,unrelated_code()должен продолжать работу даже в случае таймаута. Это реализовано с помощьюuncancel(). Блоки-менеджерыTaskGroupиспользуютuncancel()аналогичным образом.Если код конечного пользователя по какой-то причине подавляет отмену, перехватывая
CancelledError, он должен вызвать этот метод, чтобы удалить состояние отмены.
-
cancelling() -
Возвращает количество ожидающих запросов на отмену для этой задачи, то есть количество вызовов
cancel()минус количество вызововuncancel().Обратите внимание, что если это число больше нуля, но задача всё ещё выполняется,
cancelled()всё равно вернётFalse. Это связано с тем, что это число может быть уменьшено вызовомuncancel(), что может привести к тому, что задача не будет отменена, если запросы на отмену станут равны нулю.Этот метод используется внутренними механизмами asyncio и не должен использоваться кодом конечного пользователя. См.
uncancel()для получения дополнительной информации.Добавлена в версии 3.11.
-
© 2001–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.12/library/asyncio-task.html