Сопрограммы и задачи

В этом разделе описываются высокоуровневые API asyncio для работы с сопрограммами и задачами.

Сопрограммы

Исходный код: Lib/asyncio/coroutines.py


Coroutines объявление с использованием синтаксиса 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() для одновременного запуска сопрограмм как asyncio Tasks.

    Давайте изменим приведенный выше пример и запустим две 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.

Ожидаемые

Мы говорим, что объект является ** ожидаемым** объектом, если он может быть использован в выражении await. Многие API-интерфейсы asyncio предназначены для приема доступных.

Существует три основных типа ожидаемых объектов: сопрограммы, Задачи и Фьючерсы.

Сопрограммы

Сопрограммы Python являются ожидаемыми и, следовательно, их можно ожидать от других сопрограмм:

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())

Важно

В этой документации термин «сопрограмма» может использоваться для обозначения двух тесно связанных понятий:

  • a функция сопрограммы: функция 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 - это специальный низкоуровневый ожидаемый объект, который представляет конечный результат асинхронной операции.

Когда объект Future находится в режиме ожидания, это означает, что сопрограмма будет ждать, пока будущее не будет решено в каком-либо другом месте.

Будущие объекты в asyncio необходимы для того, чтобы разрешить использование кода на основе обратного вызова с асинхронным ожиданием.

Обычно ** нет необходимости ** создавать будущие объекты в коде прикладного уровня.

Можно ожидать появления будущих объектов, иногда предоставляемых библиотеками и некоторыми 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()
    )

Хорошим примером низкоуровневой функции, возвращающей будущий объект, является loop.run_in_executor().

Создание задач

Исходный код: Lib/asyncio/tasks.py


asyncio.create_task(coro, *, name=None, context=None)

Оберните coro coroutine в 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

asynchronous context manager содержит группу задач. Задачи могут быть добавлены в группу с помощью 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("Both tasks have completed now.")

Инструкция 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)

Блокировка на задержку в секундах.

Если указан 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)

Запустите awaitable objects в последовательности aws одновременно.

Если какая-либо из доступных в aws сопрограмм является сопрограммой, она автоматически планируется как задача.

Если все доступные значения выполнены успешно, результатом будет сводный список возвращаемых значений. Порядок результирующих значений соответствует порядку ожидаемых значений в aws.

Если значение return_exceptions равно False (по умолчанию), то первое возникшее исключение немедленно передается задаче, ожидающей gather(). Другие ожидаемые события в последовательности aws не будут отменены** и продолжат выполняться.

Если значение return_exceptions равно True, исключения обрабатываются так же, как и успешные результаты, и суммируются в списке результатов.

Если значение gather() отменено, все отправленные заявки (которые еще не завершены) также отменяются.

Если какая-либо задача или функция из последовательности aws отменена*, это рассматривается так, как если бы она вызвала CancelledError - вызов gather() в этом случае не отменяется. Это делается для того, чтобы отмена одной отправленной задачи в будущем не привела к отмене других задач в будущем.

Примечание

Более современным способом одновременного создания и выполнения задач и ожидания их завершения является asyncio.TaskGroup.

Пример:

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 равно False, отмена функции gather() после того, как она была помечена как выполненная, не приведет к отмене отправленных ожидаемых объектов. Например, gather может быть помечен как выполненный после передачи исключения вызывающему объекту, поэтому вызов gather.cancel() после перехвата исключения (вызванного одним из ожидаемых объектов) из gather не отменит никаких других ожидаемых объектов.

Изменено в версии 3.7: Если сам сбор отменен, отмена распространяется независимо от return_exceptions.

Изменено в версии 3.10: Удален параметр loop.

Не рекомендуется, начиная с версии 3.10: Предупреждение об устаревании выдается, если позиционные аргументы не указаны или не все позиционные аргументы являются объектами, подобными будущему, и нет запущенного цикла обработки событий.

Защита от Аннулирования

awaitable asyncio.shield(aw)

Защитите awaitable object от 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 не является объектом, похожим на будущее, и нет запущенного цикла обработки событий.

Тайм-ауты

asyncio.timeout(delay)

Возвращает значение asynchronous context manager, которое можно использовать для ограничения времени, затрачиваемого на ожидание чего-либо.

задержка может быть равна None или плавающему числу секунд ожидания. Если задержка равна None, ограничение по времени применяться не будет; это может быть полезно, если задержка неизвестна при создании контекстного менеджера.

В любом случае, контекстный менеджер может быть перенесен после создания с помощью 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)

asynchronous context manager для отмены просроченных сопрограмм.

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(), за исключением *, когда * - это абсолютное время для прекращения ожидания, или 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 awaitable с истечением времени ожидания.

Если aw является сопрограммой, то она автоматически планируется как задача.

тайм-аут может быть равен None или количеству секунд ожидания с плавающей запятой или int. Если тайм-аут равен None, заблокируйте до завершения future.

Если наступает тайм-аут, задача отменяется и выводится значение TimeoutError.

Чтобы избежать выполнения задачи cancellation, оберните ее в shield().

Функция будет ждать, пока future не будет фактически отменен, поэтому общее время ожидания может превысить тайм-аут. Если во время отмены происходит исключение, оно распространяется.

Если ожидание отменено, то будущий 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 iterable одновременно и блокируйте до тех пор, пока не будет выполнено условие, указанное в return_when.

Поле aws iterable не должно быть пустым, и генераторы, выдающие задачи, не принимаются.

Возвращает два набора TasksFutures: (done, pending).

Использование:

done, pending = await asyncio.wait(aws)

timeout (значение с плавающей точкой или int), если указано, может использоваться для управления максимальным количеством секунд ожидания перед возвратом.

Обратите внимание, что эта функция не вызывает TimeoutError. Будущие задачи, которые не были выполнены по истечении времени ожидания, просто возвращаются во втором наборе.

return_when указывает, когда эта функция должна вернуться. Это должна быть одна из следующих констант:

Постоянный

Описание

asyncio.FIRST_COMPLETED

Функция будет возвращена, когда все последующие действия завершатся или будут отменены.

asyncio.FIRST_EXCEPTION

Функция вернет значение, когда любое будущее завершится созданием исключения. Если ни одно будущее не вызовет исключения, то это эквивалентно ALL_COMPLETED.

asyncio.ALL_COMPLETED

Функция вернется, когда все фьючерсы завершатся или будут отменены.

В отличие от wait_for(), wait() не отменяет фьючерсы при истечении времени ожидания.

Изменено в версии 3.10: Удален параметр loop.

Изменено в версии 3.11: Прямая передача объектов сопрограммы в wait() запрещена.

asyncio.as_completed(aws, *, timeout=None)

Запустите awaitable objects в aws iterable одновременно. Генераторы, выдающие задачи, не принимаются в качестве aws iterable. Возвращает итератор сопрограмм. Можно ожидать, что каждая возвращенная сопрограмма получит самый ранний следующий результат из оставшихся доступных итераций.

Увеличивает значение TimeoutError, если тайм-аут наступает до того, как будут выполнены все фьючерсы.

Пример:

for coro in as_completed(aws):
    earliest_result = await coro
    # ...

Изменено в версии 3.10: Удален параметр loop.

Не рекомендуется, начиная с версии 3.10: Предупреждение об устаревании выдается, если не все доступные объекты в aws iterable являются объектами, подобными будущим, и нет запущенного цикла обработки событий.

Запуск в потоках

coroutine asyncio.to_thread(func, /, *args, **kwargs)

Асинхронно запустите функцию func в отдельном потоке.

Любые *arg и **kwarg, предоставленные для этой функции, напрямую передаются в 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}')

Смотрите раздел concurrency and multithreading документации.

В отличие от других функций asyncio, для этой функции требуется явная передача аргумента loop.

Добавлено в версии 3.5.1.

Самоанализ

asyncio.current_task(loop=None)

Возвращает текущий запущенный экземпляр Task или None, если ни одна задача не запущена.

If loop is None get_running_loop() используется для получения текущего цикла.

Добавлено в версии 3.7.

asyncio.all_tasks(loop=None)

Возвращает набор еще не завершенных Task объектов, запущенных циклом.

If loop is None, get_running_loop() используется для получения текущего цикла.

Добавлено в версии 3.7.

asyncio.iscoroutine(obj)

Возвращает True, если obj является объектом сопрограммы.

Добавлено в версии 3.4.

Объект задачи

class asyncio.Task(coro, *, loop=None, name=None, context=None)

Объект Future-like, который запускает Python coroutine. Не является потокобезопасным.

Задачи используются для запуска сопрограмм в циклах событий. Если сопрограмма ожидает выполнения в будущем, задача приостанавливает выполнение сопрограммы и ожидает завершения выполнения в будущем. Когда будущее будет выполнено, выполнение завершенной сопрограммы возобновится.

В циклах обработки событий используется совместное планирование: цикл обработки событий запускает одну задачу за раз. Пока задача ожидает завершения в будущем, цикл обработки событий запускает другие задачи, обратные вызовы или выполняет операции ввода-вывода.

Для создания задач используйте высокоуровневую функцию asyncio.create_task() или низкоуровневые функции loop.create_task() или ensure_future(). Не рекомендуется создавать экземпляры задач вручную.

Чтобы отменить запущенную задачу, используйте метод cancel(). Его вызов приведет к тому, что задача создаст исключение CancelledError в завершенной сопрограмме. Если во время отмены для будущего объекта ожидается выполнение сопрограммы, будущий объект будет отменен.

cancelled() может использоваться для проверки того, была ли задача отменена. Метод возвращает True, если завершенная сопрограмма не подавила исключение CancelledError и была фактически отменена.

asyncio.Task наследует от Future все его API, кроме Future.set_result() и Future.set_exception().

Необязательный аргумент context, содержащий только ключевое слово, позволяет указать пользовательский contextvars.Context для запуска coro. Если context не указан, задача копирует текущий контекст и позже запускает свою сопрограмму в скопированном контексте.

Изменено в версии 3.7: Добавлена поддержка модуля contextvars.

Изменено в версии 3.8: Добавлен параметр name.

Не рекомендуется, начиная с версии 3.10: Предупреждение об устаревании выдается, если loop не указан и нет запущенного цикла обработки событий.

Изменено в версии 3.11: Добавлен параметр context.

done()

Верните True, если задача выполнена.

Задача считается выполненной, когда завершенная сопрограмма либо вернула значение, либо вызвала исключение, либо задача была отменена.

result()

Верните результат выполнения задачи.

Если задача выполнена, возвращается результат завершенной сопрограммы (или, если сопрограмма вызвала исключение, это исключение вызывается повторно).

Если задача была отменена, этот метод вызывает исключение CancelledError.

Если результат задачи еще недоступен, этот метод вызывает исключение InvalidStateError.

exception()

Возвращает исключение из задачи.

Если завершенная сопрограмма вызвала исключение, то это исключение возвращается. Если завершенная сопрограмма вернула результат в обычном режиме, этот метод возвращает None.

Если задача была отменена, этот метод вызывает исключение CancelledError.

Если задача еще не выполнена, этот метод вызывает исключение InvalidStateError.

add_done_callback(callback, *, context=None)

Добавьте обратный вызов, который будет запущен, когда задача будет выполнена.

Этот метод следует использовать только в низкоуровневом коде, основанном на обратном вызове.

Смотрите документацию по Future.add_done_callback() для получения более подробной информации.

remove_done_callback(callback)

Удалите обратный вызов из списка обратных вызовов.

Этот метод следует использовать только в низкоуровневом коде, основанном на обратном вызове.

Смотрите документацию по Future.remove_done_callback() для получения более подробной информации.

get_stack(*, limit=None)

Возвращает список фреймов стека для этой задачи.

Если завершенная сопрограмма не выполнена, возвращается стек, в котором она была приостановлена. Если сопрограмма успешно завершилась или была отменена, возвращается пустой список. Если сопрограмма была прервана из-за исключения, возвращается список фреймов обратной трассировки.

Рамки всегда упорядочиваются от самых старых к самым новым.

Для приостановленной сопрограммы возвращается только один кадр стека.

Необязательный аргумент limit устанавливает максимальное количество возвращаемых кадров; по умолчанию возвращаются все доступные кадры. Порядок возвращаемого списка отличается в зависимости от того, возвращается ли стек или обратная трассировка: возвращаются самые новые кадры стека, но возвращаются самые старые кадры обратной трассировки. (Это соответствует поведению модуля обратной трассировки.)

print_stack(*, limit=None, file=None)

Распечатайте стек или обратную трассировку для этой задачи.

Это приводит к выводу, аналогичному результату модуля обратной трассировки для кадров, полученных с помощью get_stack().

Аргумент limit передается непосредственно в get_stack().

Аргумент file - это поток ввода-вывода, в который записываются выходные данные; по умолчанию выходные данные записываются в sys.stdout.

get_coro()

Возвращает объект сопрограммы, заключенный в Task.

Добавлено в версии 3.8.

get_name()

Верните название задачи.

Если задаче не было явно присвоено имя, реализация задачи asyncio по умолчанию генерирует имя по умолчанию во время создания экземпляра.

Добавлено в версии 3.8.

set_name(value)

Задайте название задачи.

Аргументом value может быть любой объект, который затем преобразуется в строку.

В реализации задачи по умолчанию имя будет отображаться в repr() выходных данных объекта задачи.

Добавлено в версии 3.8.

cancel(msg=None)

Запросите отмену задания.

Это приводит к тому, что в завершенной сопрограмме в следующем цикле цикла обработки событий генерируется исключение CancelledError.

В этом случае у сопрограммы есть шанс очистить или даже отклонить запрос, подавив исключение с помощью try … … except CancelledErrorfinally блокировать. Таким образом, в отличие от 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.

Вернуться на верх