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

В чем различие между create_task и await в AsyncIO?

1.7 Middle🔥 181 комментариев
#Python Core#Асинхронность и многопоточность

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

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

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

Различие между create_task и await в AsyncIO

Это фундаментальный вопрос в асинхронном программировании на Python. Разница между ними критична для правильного использования asyncio.

Быстрый ответ

# ❌ НЕПРАВИЛЬНО: блокирует выполнение
await coroutine()  # Ждём результата, потом продолжаем

# ✅ ПРАВИЛЬНО: запускает параллельно
task = asyncio.create_task(coroutine())  # Запускаем и сразу продолжаем

1. await — Последовательное выполнение

import asyncio
import time

async def fetch_data(url, delay):
    """Имитирует сетевой запрос"""
    print(f"Fetching {url}...")
    await asyncio.sleep(delay)  # Ждём delay секунд
    print(f"Got data from {url}")
    return f"Data from {url}"

async def main_with_await():
    start = time.time()
    
    # Способ 1: использовать await
    result1 = await fetch_data("api1.com", 2)  # Ждём 2 секунды
    result2 = await fetch_data("api2.com", 2)  # Потом ждём ещё 2 секунды
    result3 = await fetch_data("api3.com", 2)  # И ещё 2 секунды
    
    elapsed = time.time() - start
    print(f"\nTotal time: {elapsed:.1f}s")
    # Output:
    # Fetching api1.com...
    # Got data from api1.com
    # Fetching api2.com...
    # Got data from api2.com
    # Fetching api3.com...
    # Got data from api3.com
    # Total time: 6.0s  ⚠️ 6 СЕКУНД! Последовательно

asyncio.run(main_with_await())

Что происходит с await:

  1. Запускаем fetch_data("api1.com", 2)
  2. Ждём завершения (2 секунды)
  3. Продолжаем с fetch_data("api2.com", 2)
  4. Ждём завершения (2 секунды)
  5. Продолжаем с fetch_data("api3.com", 2)
  6. Ждём завершения (2 секунды)
  7. Итого: 6 секунд (2+2+2)

2. create_task — Параллельное выполнение

async def main_with_create_task():
    start = time.time()
    
    # Способ 2: использовать create_task
    task1 = asyncio.create_task(fetch_data("api1.com", 2))  # Запускаем
    task2 = asyncio.create_task(fetch_data("api2.com", 2))  # Запускаем
    task3 = asyncio.create_task(fetch_data("api3.com", 2))  # Запускаем
    
    # Теперь они все выполняются одновременно!
    result1 = await task1  # Ждём первого
    result2 = await task2  # Ждём второго
    result3 = await task3  # Ждём третьего
    
    elapsed = time.time() - start
    print(f"\nTotal time: {elapsed:.1f}s")
    # Output:
    # Fetching api1.com...
    # Fetching api2.com...
    # Fetching api3.com...
    # Got data from api1.com
    # Got data from api2.com
    # Got data from api3.com
    # Total time: 2.0s  ✅ 2 СЕКУНДЫ! Параллельно

asyncio.run(main_with_create_task())

Что происходит с create_task:

  1. Запускаем все три задачи (они стартуют сразу)
  2. Все три выполняются одновременно
  3. Ждём первого (максимум 2 секунды, но все три уже работают)
  4. Итого: 2 секунды (максимум из трёх параллельных)

3. Визуализация разницы

await версия (последовательно):
Task1: |-----|
Task2:       |-----|
Task3:             |-----|
Time: 0     2      4     6  (секунды)
Утечение времени: 6 секунд

create_task версия (параллельно):
Task1: |-----|
Task2: |-----|
Task3: |-----|
Time: 0     2      4  (секунды)
Утечение времени: 2 секунды

4. Синтаксические различия

import asyncio

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

async def main():
    # ============================================
    # Способ 1: await — ждём результат сразу
    # ============================================
    result = await my_coroutine()  # Ждём результат
    print(result)  # Вывелся сразу
    
    # ============================================
    # Способ 2: create_task — создаём задачу
    # ============================================
    task = asyncio.create_task(my_coroutine())  # Создаём task
    # Здесь корутина уже выполняется в фоне!
    result = await task  # Ждём результат
    print(result)  # Вывелся сразу
    
    # ============================================
    # Способ 3: gather для нескольких await
    # ============================================
    results = await asyncio.gather(
        my_coroutine(),
        my_coroutine(),
        my_coroutine()
    )  # Все три выполняются параллельно
    print(results)  # ["Done", "Done", "Done"]

asyncio.run(main())

5. Когда использовать create_task

Использовай create_task когда нужна параллельность

# ✅ ПРАВИЛЬНО: параллельные сетевые запросы
async def fetch_multiple_urls():
    tasks = [
        asyncio.create_task(fetch_from_api(url))
        for url in ["api1.com", "api2.com", "api3.com"]
    ]
    results = await asyncio.gather(*tasks)
    return results

# ✅ ПРАВИЛЬНО: фоновые задачи
async def server():
    background_task = asyncio.create_task(cleanup_old_files())
    # Сервер работает, cleanup выполняется в фоне
    await serve_requests()
    # После завершения основной работы
    await background_task

# ✅ ПРАВИЛЬНО: долгоживущие задачи
async def main():
    # Запустить мониторинг и основное приложение параллельно
    monitor_task = asyncio.create_task(monitor_health())
    app_task = asyncio.create_task(run_app())
    
    await asyncio.gather(monitor_task, app_task)

6. Когда использовать await

Используй await когда нужна строгая последовательность

# ✅ ПРАВИЛЬНО: последовательные зависящие операции
async def create_user():
    user_data = await fetch_user_schema()  # Зависит от следующей
    user = await save_user(user_data)      # Зависит от предыдущей
    await send_email(user)                 # Зависит от предыдущей
    return user

# ✅ ПРАВИЛЬНО: одна операция с результатом
async def get_single_value():
    result = await fetch_from_db()  # Нужен результат
    return result * 2  # Используем результат

# ✅ ПРАВИЛЬНО: когда задачи зависимы
async def pipeline():
    # Каждый шаг зависит от результата предыдущего
    step1 = await process_step1()  # Выводит 100
    step2 = await process_step2(step1)  # Использует 100
    step3 = await process_step3(step2)  # Использует результат

7. Сравнение подходов

async def demo():
    """Сравнение различных подходов"""
    
    # ❌ МЕДЛЕННО: последовательный await (6 секунд)
    await task1()
    await task2()
    await task3()
    
    # ✅ БЫСТРО: параллельный create_task (2 секунды)
    t1 = asyncio.create_task(task1())
    t2 = asyncio.create_task(task2())
    t3 = asyncio.create_task(task3())
    await asyncio.gather(t1, t2, t3)
    
    # ✅ БЫСТРО: gather напрямую (2 секунды, но меньше кода)
    await asyncio.gather(task1(), task2(), task3())
    
    # ✅ БЫСТРО: TaskGroup (Python 3.11+)
    async with asyncio.TaskGroup() as tg:
        tg.create_task(task1())
        tg.create_task(task2())
        tg.create_task(task3())

8. Обработка исключений

# ❌ ПЛОХО: await скрывает исключение
async def bad_error_handling():
    try:
        await failing_coroutine()  # Если бросит исключение
    except Exception as e:
        print(f"Caught: {e}")

# ✅ ПРАВИЛЬНО: create_task сохраняет исключение в task
async def good_error_handling():
    task = asyncio.create_task(failing_coroutine())
    try:
        result = await task  # Исключение выведется здесь
    except Exception as e:
        print(f"Caught: {e}")

# ✅ ПРАВИЛЬНО: gather обработает исключения
async def gather_error_handling():
    try:
        results = await asyncio.gather(
            task1(),
            task2(),
            return_exceptions=True  # Вернуть исключения вместо их выброса
        )
    except Exception as e:
        print(f"One of tasks failed: {e}")

9. Реальный пример: обработка множества запросов

import asyncio
import aiohttp
from typing import List

async def fetch_user(session, user_id: int) -> dict:
    """Получить данные пользователя"""
    async with session.get(f"https://api.example.com/users/{user_id}") as resp:
        return await resp.json()

async def fetch_all_users_slow(user_ids: List[int]) -> List[dict]:
    """❌ МЕДЛЕННЫЙ СПОСОБ: последовательно"""
    results = []
    async with aiohttp.ClientSession() as session:
        for user_id in user_ids:
            user = await fetch_user(session, user_id)  # Ждём каждого
            results.append(user)
    # Время: N * latency (если latency=100ms и 100 юзеров = 10 секунд)
    return results

async def fetch_all_users_fast(user_ids: List[int]) -> List[dict]:
    """✅ БЫСТРЫЙ СПОСОБ: параллельно"""
    async with aiohttp.ClientSession() as session:
        tasks = [
            asyncio.create_task(fetch_user(session, user_id))
            for user_id in user_ids
        ]
        results = await asyncio.gather(*tasks)
    # Время: max(latency) (если latency=100ms = всего 100ms для 100 юзеров)
    return results

# Или более современный способ (Python 3.11+)
async def fetch_all_users_modern(user_ids: List[int]) -> List[dict]:
    """✅ СОВРЕМЕННЫЙ СПОСОБ: TaskGroup"""
    async with aiohttp.ClientSession() as session:
        async with asyncio.TaskGroup() as tg:
            tasks = [
                tg.create_task(fetch_user(session, user_id))
                for user_id in user_ids
            ]
        return [task.result() for task in tasks]

10. Таблица сравнения

Аспектawaitcreate_task
Способ запускаСинхронныйАсинхронный
Когда стартуетСейчасСразу в фоне
РезультатВозвращаетсяНужно await
Параллельность❌ Нет (последовательно)✅ Да (параллельно)
Время выполненияsum(времена задач)max(времена задач)
Когда использоватьЗависимые задачиНезависимые задачи
Синтаксисresult = await func()task = asyncio.create_task(func())
ИсключенияВыбрасываются сразуСохраняются в task

11. Рекомендации

# ✅ BEST PRACTICES:

# 1. Используй create_task для параллельных операций
for url in urls:
    asyncio.create_task(fetch(url))  # Все запустятся параллельно

# 2. Используй await для зависимых операций
user_data = await fetch_user()  # Нужны данные перед следующим
user_profile = await fetch_profile(user_data)  # Зависит от предыдущего

# 3. Используй gather для нескольких параллельных task'ов
results = await asyncio.gather(
    fetch(url1),
    fetch(url2),
    fetch(url3)
)

# 4. В Python 3.11+ используй TaskGroup
async with asyncio.TaskGroup() as tg:
    for url in urls:
        tg.create_task(fetch(url))

# 5. Никогда не забывай await на результат task'а
task = asyncio.create_task(my_coroutine())
result = await task  # Обязательно await!

Итоговый вывод

await — это синхронное ожидание результата корутины. Используется когда результат нужен сразу для следующей операции.

create_task — это запуск корутины в фоне. Используется когда нужна параллельность и можно продолжить работу, пока задача выполняется.

Ключевой момент:

  • await = ЖДЁШЬ (последовательно)
  • create_task = ЗАПУСКАЕШЬ В ФОНЕ (параллельно)

Для производительности сетевых приложений — всегда используй create_task или gather для параллельных операций. Это может ускорить приложение в 10+ раз!

В чем различие между create_task и await в AsyncIO? | PrepBro