Spec-Zone.ru › Python 3.11

Потоки и задачи

В этом разделе описываются 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() для одновременного выполнения потоков как задач 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.

Объекты-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() для полного удаления состояния отмены.

END_OF_DOCUMENT_MARKER ```

Группы задач

Группы задач объединяют 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("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)

Задержка на 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)

Запускает awaitable объекты в последовательности aws параллельно.

Если любой awaitable объект в aws является корутиной, она автоматически планируется как задача.

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

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

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

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

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

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

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

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

Защита от отмены

awaitable asyncio.shield(aw)

Защищает awaitable объект от отмены с помощью 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 и нет работающего цикла событий.

END_OF_DOCUMENT_MARKER

Таймауты

asyncio.timeout(delay)

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

delay может быть None, или числом с плавающей точкой/целым числом секунд для ожидания. Если delay 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)

Асинхронный менеджер контекста для отмены просроченных корутин.

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 awaitable объекта с таймаутом.

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

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

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

Константа

Описание

FIRST_COMPLETED

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

FIRST_EXCEPTION

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

ALL_COMPLETED

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

В отличие от wait_for(), wait() не отменяет будущие при возникновении таймаута.

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

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

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

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

Возникает TimeoutError, если таймаут произойдёт до завершения всех будущих.

Пример:

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

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

Устарело начиная с версии 3.10: Выводится предупреждение об устаревании, если не все awaitable объекты в итерируемом aws являются объектами типа Future, и нет работающего цикла событий.

Выполнение в потоках

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

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

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 является None, используется get_running_loop() для получения текущего цикла.

Новое в версии 3.7.

asyncio.all_tasks(loop=None)

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

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

Новое в версии 3.7.

asyncio.iscoroutine(obj)

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

Новое в версии 3.4.

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

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

Объект 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 не указан, задача копирует текущий контекст и затем выполняет свою корутину в скопированном контексте.

Изменено в версии 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)

Удаляет 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.

Новое в версии 3.8.

get_name()

Возвращает имя задачи.

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

Новое в версии 3.8.

set_name(value)

Устанавливает имя задачи.

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

В стандартной реализации Task имя будет видно в выводе 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–2023 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.11/library/asyncio-task.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API