Что представляет собой объект Task в asyncio и как его создать

Ответ

asyncio.Task — это объект, который планирует и управляет выполнением асинхронной функции (корутины) в цикле событий. Он позволяет запускать корутины конкурентно, не дожидаясь завершения одной, чтобы начать другую.

Ключевое отличие от прямого await:

  • await my_coro(): Последовательное выполнение. Программа остановится и будет ждать завершения my_coro(), прежде чем перейти к следующей строке кода.
  • task = asyncio.create_task(my_coro()): Конкурентный запуск. Корутина my_coro() планируется для выполнения в фоновом режиме, а программа немедленно продолжает свою работу. task можно использовать позже, чтобы дождаться результата.

Как создать Task:

Основной способ — использовать функцию asyncio.create_task().

Практический пример (конкурентное выполнение):

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

import asyncio
import time

async def say_after(delay, text):
    await asyncio.sleep(delay)
    print(text)

async def main():
    start_time = time.time()
    print(f"Начало выполнения в {time.strftime('%X')}")

    # Создаем и запускаем две задачи конкурентно
    task1 = asyncio.create_task(
        say_after(1, 'Hello')
    )

    task2 = asyncio.create_task(
        say_after(2, 'World')
    )

    # Ждем завершения обеих задач
    await task1
    await task2
    # Альтернативный способ дождаться всех задач: await asyncio.gather(task1, task2)

    end_time = time.time()
    print(f"Завершено в {time.strftime('%X')}")
    print(f"Общее время выполнения: {end_time - start_time:.2f} секунд")

asyncio.run(main())

# Вывод:
# Начало выполнения в 14:30:00
# Hello
# World
# Завершено в 14:30:02
# Общее время выполнения: 2.01 секунд (а не 1+2=3)

Ответ 18+ 🔞

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

Представь себе, блядь, корутину — это как твой друг, который обещал принести пива. Если ты просто на него повесишь await, то будешь сидеть у подъезда, как лох, и ждать, пока он допиздуется из магазина. Ни почесаться, ни в телефон посмотреть — нихуя. Застыл, как истукан.

А вот Task — это, сука, гениальная идея. Ты этого друга с пивом в фоновый режим отправляешь. Создал задачу — task = asyncio.create_task(друг_с_пивом()) — и похуй. Пусть себе идёт. А сам в это время можешь чипсы открыть, музыку включить, ещё кого-нибудь позвать. Потом, когда захотел пить, делаешь await task и получаешь своё пиво. А если не захотел — можешь его вообще отменить, если он, мудак, слишком долго.

Короче, разница проще говоря:

  • await корутина() — это последовательно. Стою, жду, ебучую скуку терплю.
  • task = asyncio.create_task(корутина()) — это конкурентно. Запустил и забыл, делай дальше что хош, результат потом заберёшь.

Как создать эту самую задачу? Да элементарно, ёпта! asyncio.create_task() — твой главный инструмент.

Смотри, вот живой пример, чтобы мозг не взорвался:

Допустим, у нас две функции, которые спят и потом что-то говорят. Если делать их по очереди с await, то они сложатся, и мы будем ждать нихуя не три, а целых три секунды. Ай-яй-яй, неэффективно!

Но если завернуть их в Task — они побегут почти одновременно, и общее время будет равно времени самой долгой из них. Магия, блядь!

import asyncio
import time

async def say_after(delay, text):
    await asyncio.sleep(delay)  # Симулируем какую-то долгую работу, типа ждём ответа от сервера
    print(text)

async def main():
    start_time = time.time()
    print(f"Стартанули в {time.strftime('%X')}")

    # А вот тут фокус! Создаём две задачи и запускаем их КОНКУРЕНТНО!
    task1 = asyncio.create_task(
        say_after(1, 'Привет')
    )
    task2 = asyncio.create_task(
        say_after(2, 'Мир')
    )

    # Теперь ждём, когда обе эти штуки допиздятся
    await task1
    await task2
    # Кстати, можно было и так: await asyncio.gather(task1, task2) — одной строчкой

    end_time = time.time()
    print(f"Закончили в {time.strftime('%X')}")
    print(f"Итого на всё проёбали: {end_time - start_time:.2f} секунды")

asyncio.run(main())

# Вывод будет примерно такой:
# Стартанули в 14:30:00
# Привет  # Появилось через ~1 секунду
# Мир     # Появилось через ~2 секунды ОТ НАЧАЛА, а не после "Привет"
# Закончили в 14:30:02
# Итого на всё проёбали: 2.01 секунды (ВОТ ОН, ПИЗДЕЦ КАКОЙ ВЫИГРЫШ! А не 1+2=3)

Видишь? Вместо трёх секунд — две. Потому что пока вторая задача два секунды спала, первая уже своё дело сделала и отчиталась. Вот для этого эти Task и нужны, чтобы не проёбывать время впустую, ожидая каждую операцию по отдельности.