← Назад к вопросам

Во что оборачивается корутина перед помещением в Event Loop

2.4 Senior🔥 131 комментариев
#Асинхронность и многопоточность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Корутины и Event Loop: оборачивание в Task

Это критически важный вопрос для понимания того, как asyncio на самом деле работает.

Различие между Coroutine и Task

Корутина — это просто объект, определённый с помощью async def. Она ничего не делает самостоятельно:

import asyncio

async def my_coroutine():
    await asyncio.sleep(1)
    return "done"

# Создаём корутину
coro = my_coroutine()  # Это НЕ выполняется здесь
print(type(coro))  # <class "coroutine">

# Корутина сама по себе ничего не делает
del coro  # Просто удалили

Task — это обёртка для корутины в Event Loop:

import asyncio

async def my_coroutine():
    await asyncio.sleep(1)
    return "done"

async def main():
    # Оборачиваем в Task
    task = asyncio.create_task(my_coroutine())
    # Теперь Event Loop управляет ей
    result = await task
    return result

asyncio.run(main())

Что происходит при создании Task

Когда корутина помещается в Event Loop, она оборачивается в объект asyncio.Task:

import asyncio

async def example():
    return "done"

async def inspect():
    # Создаём Task
    task = asyncio.create_task(example())
    
    # Task содержит:
    # - _coro: сама корутина
    # - _state: PENDING, RUNNING, DONE и т.д.
    # - _result: результат выполнения
    # - _exception: исключение если есть
    
    print(f"Task: {task}")
    print(f"Состояние: {task._state}")
    print(f"Содержит корутину: {task.get_coro()}")
    
    result = await task
    print(f"Результат: {result}")

asyncio.run(inspect())

Последовательное vs Параллельное выполнение

Без оборачивания в Task — последовательно

import asyncio
import time

async def task(name):
    await asyncio.sleep(1)
    return name

async def sequential():
    start = time.time()
    
    # Без Task — ждём каждую
    r1 = await task("1")  # 1 сек
    r2 = await task("2")  # +1 сек
    r3 = await task("3")  # +1 сек
    
    print(f"Время: {time.time() - start:.1f}s")  # ~3 сек

asyncio.run(sequential())

С оборачиванием в Task — параллельно

import asyncio
import time

async def task(name):
    await asyncio.sleep(1)
    return name

async def parallel():
    start = time.time()
    
    # С Task — выполняются параллельно
    t1 = asyncio.create_task(task("1"))
    t2 = asyncio.create_task(task("2"))
    t3 = asyncio.create_task(task("3"))
    
    # Event Loop уже запустила все три
    r1 = await t1
    r2 = await t2
    r3 = await t3
    
    print(f"Время: {time.time() - start:.1f}s")  # ~1 сек

asyncio.run(parallel())

Task наследует Future

import asyncio
from asyncio import Task, Future

async def example():
    return "result"

async def compare():
    task = asyncio.create_task(example())
    
    # Task — это подкласс Future
    print(f"Task наследует Future: {isinstance(task, Future)}")
    print(f"Task наследует Task: {isinstance(task, Task)}")
    
    # Task имеет все методы Future:
    # - done(): выполнена ли
    # - result(): получить результат
    # - set_result(): установить результат
    # - cancel(): отменить
    
    await task
    print(f"done(): {task.done()}")
    print(f"result(): {task.result()}")

asyncio.run(compare())

asyncio.gather() автоматически оборачивает

import asyncio
import time

async def task(n):
    await asyncio.sleep(1)
    return n

async def main():
    start = time.time()
    
    # gather() автоматически оборачивает в Task
    results = await asyncio.gather(
        task(1),  # Task 1
        task(2),  # Task 2
        task(3)   # Task 3
    )
    
    # Все выполнились параллельно
    print(f"Результаты: {results}")
    print(f"Время: {time.time() - start:.1f}s")  # ~1 сек

asyncio.run(main())

Когда использовать create_task()

Используй asyncio.create_task() когда:

  • Нужно параллельное выполнение
  • Задачи независимы друг от друга
  • Нужно сохранить reference на Task
  • Хочешь управлять отменой (task.cancel())
import asyncio

async def main():
    # Создаём Task
    task = asyncio.create_task(some_coro())
    
    # Можем отменить её
    task.cancel()
    
    # Можем проверить состояние
    print(f"Отменена: {task.cancelled()}")

Практический пример

import asyncio

async def fetch(url):
    print(f"Fetching {url}")
    await asyncio.sleep(2)
    return f"Data from {url}"

async def main():
    # БЕЗ Task — 6 секунд
    # r1 = await fetch("url1")  # 2 сек
    # r2 = await fetch("url2")  # 2 сек
    # r3 = await fetch("url3")  # 2 сек
    
    # С Task — 2 секунды
    task1 = asyncio.create_task(fetch("url1"))
    task2 = asyncio.create_task(fetch("url2"))
    task3 = asyncio.create_task(fetch("url3"))
    
    # Все три работают одновременно
    results = await asyncio.gather(task1, task2, task3)
    print(results)

asyncio.run(main())

Итог

Корутина — просто объект, ничего не делает сама.

Task — обёртка, которая:

  • Позволяет Event Loop управлять выполнением
  • Можно запустить параллельно другие Task
  • Имеет состояние (pending, done, cancelled)
  • Хранит результат или исключение

Золотое правило:

  • await coro — последовательно
  • asyncio.create_task(coro) или asyncio.gather() — параллельно

Внутри Event Loop всё работает через Task — это ключ к пониманию asyncio.