Сообщить об ошибке.

Класс Task() модуля asyncio в Python

Управление ходом выполнения задачи

Синтаксис:

import asyncio

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

Параметры:

  • coro - сопрограмма Python,
  • loop=None - параметр цикла (с версии Python 3.10 выдается предупреждение об устаревании),
  • name=None - имя задачи.

Возвращаемое значение:

Описание:

Класс Task() модуля asyncio это объект, который запускает сопрограмму Python и похож на объект asyncio.Future, т.к. является подклассом этого объекта.

Экземпляры класса asyncio.Task лучше не создавать в потоках, т. к. они НЕ потокобезопасные.

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

Циклы событий используют совместное планирование. Другими словами, цикл событий запускает одну задачу за раз. Пока объект задачи Task ожидает готовности Future, цикл событий запускает другие задачи, обратные вызовы или выполняет операции ввода-вывода.

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

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

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

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

Задачи Task поддерживают модуль contextvars. Когда задача создается, то она копирует текущий контекст, а затем запускает свою сопрограмму в скопированном контексте.

Изменения:

Новое в Python 3.7: добавлена поддержка модуля contextvars.

Новое в Python 3.8: добавлен параметр name.

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

Методы объекта asyncio.Task().


Task.done():

Метод Task.done() возвращает True, если задача Task выполнена.

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

Task.result():

Метод Task.result() возвращает результат выполнения задачи Task.

  • Если задача выполнена, то возвращается результат обернутой сопрограммы.
  • Если сопрограмма вызвала исключение, то это исключение вызывается повторно.
  • Если задача была отменена, то метод Task.result() вызывает исключение asyncio.CancelledError.
  • Если результат задачи еще не доступен, то этот метод вызывает исключение asyncio.InvalidStateError.

Task.exception():

Метод Task.exception() возвращает исключение из задачи Task.

  • Если обернутая сопрограмма вызвала исключение, то возвращается это исключение.
  • Если обернутая сопрограмма вернула нормальный результат, то этот метод возвращает None.
  • Если задача была отменена, то метод Task.exception() вызывает исключение asyncio.CancelledError.
  • Если задача еще не выполнена, то метод вызывает исключение исключение asyncio.InvalidStateError.

Task.add_done_callback(callback, *, context=None):

Метод Task.add_done_callback() добавляет обратный вызов callback, который будет запускаться, когда задача будет выполнена.

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

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

Task.remove_done_callback(callback):

Метод Task.remove_done_callback() удаляет обратный вызов callback из списка обратных вызовов.

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

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

Task.get_stack(*, limit=None):

Метод Task.get_stack() возвращает список кадров стека для этой задачи.

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

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

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

Порядок возвращаемого списка различается в зависимости от того, возвращается ли стек или трассировка: возвращаются самые новые кадры стека, но, возвращаются самые старые кадры трассировки, что соответствует поведению модуля traceback.

Task.print_stack(*, limit=None, file=None):

Метод Task.print_stack() печатает стек или трассировку для этой задачи.

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

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

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

Task.get_coro():

Метод Task.get_coro() возвращает объект сопрограммы, обернутую в задачу Task.

Новое в Python 3.8.

Task.get_name():

Метод Task.get_name() возвращает имя name задачи Task.

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

Новое в Python 3.8.

Task.set_name(value):

Метод Task.set_name() устанавливает имя name задачи Task.

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

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

Новое в Python 3.8.

Task.cancel(msg=None):

Метод Task.cancel() создает запрос на отмену задания. Таким образом обеспечивает появления исключения asyncio.CancelledError, которое передается в обернутую сопрограмму на следующем шаге цикла событий.

Затем сопрограмма имеет шанс очистить или даже отклонить запрос на отмену, подавив исключение с помощью try ... finally.

try: 
... 
except CancelledError: 
... 
finally:
...

Следовательно, в отличие от Future.cancel(), Task.cancel() не гарантирует, что задача будет отменена, хотя полное подавление отмены не является распространенным явлением и активно не приветствуется.

Изменено в Python 3.9: Добавлен аргумент msg.

Изменено в Python 3.11: Аргумент msg передается от отмененной задачи к ее ожидающему.

В следующем примере показано, как сопрограммы могут перехватывать запрос отмены:

async def cancel_me():
    print('cancel_me(): перед ожиданием')
    try:
        # ждем 1 час
        await asyncio.sleep(3600)
    except asyncio.CancelledError:
        print('cancel_me(): отмена ожидания')
        # поднимаем еще раз 
        # перехваченное исключение
        raise
    finally:
        print('cancel_me(): после ожидания')

async def main():
    # Создаем задачу для "cancel_me()"
    task = asyncio.create_task(cancel_me())

    # Ждем 1 сек.
    await asyncio.sleep(1)

    # Даем запрос на отмену
    task.cancel()

    try:
        await task
    except asyncio.CancelledError:
        print("main(): `cancel_me()` теперь отменен.")

asyncio.run(main())

# Expected output:
#
#     cancel_me(): перед ожиданием
#     cancel_me(): отмена ожидания
#     cancel_me(): после ожидания
#     main(): cancel_me() теперь отменен.

Task.cancelled():

Метод Task.cancelled() возвращает True, если задача Task отменена.

Задача отменяется, когда отмена была запрошена с помощью метода Task.cancel() и обернутая сопрограмма распространила переданное в нее исключение asyncio.CancelledError.

Task.uncancel():

Метод Task.uncancel() уменьшает количество запросов на отмену для этой задачи. Возвращает оставшееся количество запросов на отмену.

Обратите внимание, что после завершения выполнения отмененной задачи дальнейшие вызовы Task.uncancel() неэффективны.

Новое в Python 3.11.

Этот метод используется внутренними компонентами модуля asyncio, и ожидается, что он не будет использоваться кодом конечного пользователя. В частности, если задача успешно отменена, то это позволяет элементам структурированного параллелизма, таким как группы задач и asyncio.timeout(), продолжать работу, изолируя отмену в соответствующем структурированном блоке. Например:

async def make_request_with_timeout():
    try:
        async with asyncio.timeout(1):
            # Структурированный блок, затронутый тайм-аутом:
            await make_request()
            await make_another_request()
    except TimeoutError:
        log("Здесь был тайм-аут")
    # Внешний код, не затронутый тайм-аутом:
    await unrelated_code()

В то время как блок с make_request() и make_another_request() может быть отменен из-за тайм-аута, unrelated_code() должен продолжать работать даже в случае тайм-аута. Это реализовано с помощью Task.uncancel(). Менеджеры контекста asyncio.TaskGroup используют Task.uncancel() аналогичным образом.

Task.cancelling():

Метод Task.cancelling() возвращает этой задаче количество ожидающих запросов на отмену, т. е. количество вызовов Task.cancel() за вычетом количества вызовов Task.uncancel().

Обратите внимание, что если это число больше нуля, но задача все еще выполняется, то метод Task.cancelled() все равно вернет False. Это связано с тем, что это число можно уменьшить, вызвав Task.uncancel(), что может привести к тому, что в конце концов задача не будет отменена, если количество запросов на отмену снизится до нуля.

Этот метод используется внутренними компонентами модуля asyncio, и ожидается, что он не будет использоваться кодом конечного пользователя. для более подробной информации смотрите uncancel() .

Новое в Python 3.11.

Пример создания и запуска задачи.

В приведенном ниже примере, попробуйте заменить создание задачи create_task(long_operation()) просто на await long_operation(), чтобы почувствовать разницу. Если будете выполнять этот эксперимент, то не забудьте убрать await task.

import asyncio

async def msg(text):
    # эмитируем короткую 
    # задержку в выполнении
    await asyncio.sleep(0.1)
    print(text)

async def long_operation():
    # эмитируем долгую 
    # задержку в выполнении
    print('long_operation started')
    await asyncio.sleep(3)
    print('long_operation complete')

# основной цикл программы
async def main():
    # легкая сопрограмма
    await msg('1 msg complete')

    # Здесь запустим `long_operation()`, но ждать, пока она 
    # выполнится не хотим, т.к. необходимо получить второе 
    # сообщение как можно раньше. Для этого создаем для нее задачу... 
    task = asyncio.create_task(long_operation())
    
    # легкая сопрограмма
    await msg('2 msg complete')

    # Теперь можно дождаться завершения 
    # задачи или отменить ее
    await task

if __name__ == "__main__":
    # запускаем цикл событий
    asyncio.run(main())

# 1 msg complete
# long_operation started
# 2 msg complete
# long_operation complete

Устаревшие методы.

Task.all_tasks(loop=None):

Метод Task.all_tasks() представляет собой метод класса, который возвращает множество всех задач для текущего цикла событий.

По умолчанию возвращаются все задачи. Если аргумент loop равен None, то для получения текущего цикла используется функция asyncio.get_event_loop().

Внимание! Метод устарел с версии Python 3.7 и удален в Python 3.9: не вызывайте его как метод задачи, вместо него используйте функцию asyncio.all_tasks().

Task.current_task(loop=None):

Метод Task.current_task() представляет собой метод класса, который возвращает текущую запущенную задачу или None.

Если аргумент loop=None, то для получения текущего цикла используется функция asyncio.get_event_loop().

Внимание! Метод устарел с версии Python 3.7 и удален в Python 3.9: не вызывайте его как метод задачи, вместо него используйте функцию asyncio.current_task().