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

Как работает асинхронный код?

2.2 Middle🔥 231 комментариев
#Асинхронность и многопоточность

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

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

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

# Как работает асинхронный код?

Асинхронное программирование позволяет выполнять несколько операций параллельно без блокирования потока выполнения. Это критически важно для высокопроизводительных приложений.

Синхронный vs Асинхронный код

Синхронный код (блокирующий)

import requests

def fetch_data(url):
    response = requests.get(url)  # Блокируется до получения ответа
    return response.json()

# Вызов занимает сумму времени всех запросов
data1 = fetch_data("https://api.example.com/1")  # 1 сек
data2 = fetch_data("https://api.example.com/2")  # 1 сек
# Всего: 2 сек

Асинхронный код (неблокирующий)

import asyncio
import aiohttp

async def fetch_data(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:  # Не блокируется
            return await response.json()

async def main():
    # Запускаем оба запроса параллельно
    tasks = [
        fetch_data("https://api.example.com/1"),
        fetch_data("https://api.example.com/2"),
    ]
    data1, data2 = await asyncio.gather(*tasks)
    # Всего: 1 сек (параллельное выполнение)

asyncio.run(main())

Event Loop (Цикл событий)

Event Loop — это сердце асинхронного программирования. Это бесконечный цикл, который:

  1. Ищет готовые корутины
  2. Выполняет их
  3. Если корутина ждёт (I/O операция), переходит к другой
  4. Возвращается к ней когда операция завершится
import asyncio

async def task1():
    print("Task 1: Начало")
    await asyncio.sleep(2)  # Имитация I/O операции
    print("Task 1: Конец")

async def task2():
    print("Task 2: Начало")
    await asyncio.sleep(1)
    print("Task 2: Конец")

async def main():
    # Создаёшь event loop и запускаешь корутины
    await asyncio.gather(task1(), task2())
    # Task 1: Начало
    # Task 2: Начало
    # Task 2: Конец (через 1 сек)
    # Task 1: Конец (через 2 сек)

asyncio.run(main())

Корутины (Coroutines)

Корутина — это функция, которая может быть приостановлена и возобновлена:

# Определение корутины
async def greet(name):
    print(f"Hello, {name}")
    await asyncio.sleep(1)  # Приостановка
    print(f"Goodbye, {name}")

# Запуск
asyncio.run(greet("Alice"))

async/await синтаксис

  • async — определяет асинхронную функцию
  • await — приостанавливает выполнение до завершения асинхронной операции
async def get_user(user_id):
    # Имитация запроса к БД
    await asyncio.sleep(1)
    return {"id": user_id, "name": "John"}

async def main():
    # await можно использовать только внутри async функции
    user = await get_user(123)
    print(user)  # {'id': 123, 'name': 'John'}

asyncio.run(main())

Параллельное выполнение

asyncio.gather()

async def main():
    # Запускаешь несколько корутин параллельно
    results = await asyncio.gather(
        get_user(1),
        get_user(2),
        get_user(3),
    )
    print(results)  # Все результаты

asyncio.create_task()

async def main():
    # Создаёшь задачи явно
    task1 = asyncio.create_task(get_user(1))
    task2 = asyncio.create_task(get_user(2))
    
    # Можешь сделать что-то ещё
    print("Загружаю данные...")
    
    # Ждёшь завершения
    result1 = await task1
    result2 = await task2
    print(result1, result2)

asyncio.as_completed()

async def main():
    tasks = [get_user(i) for i in range(1, 4)]
    
    # Обрабатываешь результаты по мере их готовности
    for task in asyncio.as_completed(tasks):
        result = await task
        print(f"Получен результат: {result}")

Обработка ошибок

async def risky_operation():
    await asyncio.sleep(1)
    raise ValueError("Something went wrong")

async def main():
    try:
        await risky_operation()
    except ValueError as e:
        print(f"Перехвачена ошибка: {e}")
    
    # Или при использовании gather()
    try:
        await asyncio.gather(
            get_user(1),
            risky_operation(),
            return_exceptions=True  # Не прерывает остальные
        )
    except Exception as e:
        print(f"Ошибка: {e}")

Асинхронный контекстный менеджер

class AsyncResource:
    async def __aenter__(self):
        print("Открываю ресурс")
        await asyncio.sleep(1)
        return self
    
    async def __aexit__(self, exc_type, exc_val, exc_tb):
        print("Закрываю ресурс")
        await asyncio.sleep(0.5)

async def main():
    async with AsyncResource() as resource:
        print("Использую ресурс")
        await asyncio.sleep(1)

Асинхронный итератор

class AsyncCounter:
    def __init__(self, max):
        self.max = max
        self.count = 0
    
    def __aiter__(self):
        return self
    
    async def __anext__(self):
        if self.count < self.max:
            self.count += 1
            await asyncio.sleep(0.1)  # Имитация I/O
            return self.count
        raise StopAsyncIteration

async def main():
    async for num in AsyncCounter(3):
        print(num)  # 1, 2, 3

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

from fastapi import FastAPI
import asyncio

app = FastAPI()

@app.get("/users/{user_id}")
async def get_user(user_id: int):
    # Асинхронный запрос к БД
    await asyncio.sleep(1)  # Имитация запроса
    return {"id": user_id, "name": "John"}

@app.get("/combined/{user_id}")
async def get_combined(user_id: int):
    # Параллельное выполнение нескольких операций
    user_task = get_user_from_db(user_id)
    posts_task = get_posts_from_db(user_id)
    
    user, posts = await asyncio.gather(user_task, posts_task)
    return {"user": user, "posts": posts}

Лучшие практики

  1. Используй asyncio.gather() для параллельного выполнения нескольких корутин
  2. Никогда не используй time.sleep() в async коде — используй await asyncio.sleep()
  3. Обрабатывай ошибки с помощью try/except
  4. Не смешивай sync и async код — это может привести к deadlock'ам
  5. Профилируй асинхронный код — убедись, что он действительно параллелен
  6. Используй асинхронные библиотеки (aiohttp, asyncpg, motor)

Отличие от многопоточности

  • Асинхронное программирование — один поток, но много задач (кооперативная многозадачность)
  • Многопоточность — несколько потоков, работают параллельно (требует синхронизации)

Асинхронное программирование обычно эффективнее для I/O операций.

Заключение

Асинхронное программирование — это мощный инструмент для создания высокопроизводительных приложений. Правильное понимание event loop, корутин и await критически важно для современного Python разработчика.

Как работает асинхронный код? | PrepBro