← Назад к вопросам
Что такое корутина?
2.0 Middle🔥 221 комментариев
#Python Core#Асинхронность и многопоточность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Корутины в Python
Определение
Корутина — это обобщение функции, которая может приостанавливаться и возобновляться в произвольных точках. В отличие от обычной функции, которая выполняется от начала до конца, корутина может отдать управление и позже продолжить с того же места.
История: от генераторов к async/await
# Этап 1: Генераторы (yield) — первая форма корутин
def simple_generator():
print("Начало")
yield 1
print("Середина")
yield 2
print("Конец")
yield 3
gen = simple_generator()
print(next(gen)) # Начало, затем 1
print(next(gen)) # Середина, затем 2
print(next(gen)) # Конец, затем 3
# Этап 2: Асинхронные корутины (async/await) — современный способ
async def async_coroutine():
print("Начало")
await asyncio.sleep(1)
print("После ожидания")
return "Результат"
Простые корутины с yield
# Корутина как производитель данных
def count_up_to(max):
count = 1
while count <= max:
print(f"Генерирую {count}")
yield count
count += 1
for num in count_up_to(3):
print(f"Получил: {num}")
# Вывод:
# Генерирую 1
# Получил: 1
# Генерирую 2
# Получил: 2
# Генерирую 3
# Получил: 3
Асинхронные корутины с async/await
import asyncio
# Объявление асинхронной корутины
async def fetch_data(url):
"""Асинхронная корутина для загрузки данных"""
print(f"Начинаю загрузку {url}")
await asyncio.sleep(2) # Имитация сетевого запроса
print(f"Загрузил {url}")
return f"Данные с {url}"
# Запуск корутины
async def main():
# Способ 1: ждём результат
result = await fetch_data("https://api.example.com")
print(result)
# Способ 2: запускаем параллельно
task1 = asyncio.create_task(fetch_data("URL1"))
task2 = asyncio.create_task(fetch_data("URL2"))
results = await asyncio.gather(task1, task2)
print(results)
# Запуск
asyncio.run(main())
Ключевые различия
# ФУНКЦИЯ — выполняется полностью от начала до конца
def regular_function():
return "Результат"
result = regular_function() # Сразу получаем результат
# ГЕНЕРАТОР — может быть приостановлен
def generator_function():
yield "Первое"
yield "Второе"
yield "Третье"
gen = generator_function() # Не выполнилось! Просто создали объект
print(next(gen)) # Первое — выполнилось до первого yield
print(next(gen)) # Второе — выполнилось до второго yield
# АСИНХРОННАЯ КОРУТИНА — выполняется с await
async def async_function():
print("Старт")
await asyncio.sleep(1)
print("После ожидания")
return "Результат"
result = await async_function() # Требует await, может выполняться параллельно
Практический пример: параллельная загрузка
import asyncio
import time
async def fetch_user(user_id):
print(f"Загружаю пользователя {user_id}")
await asyncio.sleep(2) # Имитация API запроса
print(f"Загрузил пользователя {user_id}")
return {"id": user_id, "name": f"User{user_id}"}
async def fetch_posts(user_id):
print(f"Загружаю посты пользователя {user_id}")
await asyncio.sleep(3) # Имитация API запроса
print(f"Загрузил посты пользователя {user_id}")
return [{"id": i, "user_id": user_id} for i in range(3)]
# ПЛОХО: последовательно (5 секунд)
async def bad_approach():
start = time.time()
user = await fetch_user(1)
posts = await fetch_posts(1)
print(f"Время: {time.time() - start:.2f}с") # ~5 секунд
# ХОРОШО: параллельно (3 секунды)
async def good_approach():
start = time.time()
user, posts = await asyncio.gather(
fetch_user(1),
fetch_posts(1)
)
print(f"Время: {time.time() - start:.2f}с") # ~3 секунды
asyncio.run(good_approach())
Корутины vs Потоки
import threading
import asyncio
import time
# ПОТОКИ: истинный параллелизм, но тяжело управлять
def thread_task():
for i in range(3):
print(f"Поток: {i}")
time.sleep(1)
threads = [threading.Thread(target=thread_task) for _ in range(3)]
for t in threads:
t.start()
for t in threads:
t.join()
# Каждый поток занимает память, контекстное переключение дорогое
# КОРУТИНЫ: кооперативная многозадачность, легко управлять
async def coroutine_task():
for i in range(3):
print(f"Корутина: {i}")
await asyncio.sleep(1)
async def main():
await asyncio.gather(
coroutine_task(),
coroutine_task(),
coroutine_task()
)
asyncio.run(main())
# Все корутины работают в одном потоке, но без блокировок
Цепочка вызовов корутин
import asyncio
async def step1():
print("Шаг 1: подготовка")
await asyncio.sleep(1)
return "Данные из шага 1"
async def step2(data):
print(f"Шаг 2: обработка '{data}'")
await asyncio.sleep(1)
return f"Обработано: {data}"
async def step3(data):
print(f"Шаг 3: сохранение '{data}'")
await asyncio.sleep(1)
return "Сохранено"
async def pipeline():
# Последовательная цепочка
data1 = await step1()
data2 = await step2(data1)
result = await step3(data2)
print(f"Итог: {result}")
asyncio.run(pipeline())
Обработка ошибок в корутинах
import asyncio
async def risky_operation():
await asyncio.sleep(1)
raise ValueError("Ошибка в операции!")
async def safe_operation():
try:
result = await risky_operation()
except ValueError as e:
print(f"Поймали ошибку: {e}")
return None
return result
asyncio.run(safe_operation())
Таймауты
import asyncio
async def slow_operation():
await asyncio.sleep(10)
return "Результат"
async def main():
try:
result = await asyncio.wait_for(slow_operation(), timeout=2.0)
except asyncio.TimeoutError:
print("Операция заняла слишком много времени!")
asyncio.run(main())
Сравнение синтаксиса
# Генератор (кооперативная корутина)
def generator():
yield 1
yield 2
# Асинхронная функция
async def async_func():
await asyncio.sleep(1)
return "Результат"
# Главное отличие:
# - yield: используется с for или next()
# - await: используется в async функциях
# - async function: выполняется в event loop
Когда использовать корутины?
# ✅ ИСПОЛЬЗУЙ корутины когда:
# - I/O операции (сетевые запросы, файлы)
# - Множество одновременных операций
# - Нужна асинхронность без потоков
# ❌ НЕ используй корутины когда:
# - CPU-bound задачи (использовать multiprocessing)
# - Простая синхронная логика (усложнишь)
# - Нужен истинный параллелизм (использовать потоки)
Итоговое резюме
- Корутина — функция, которая может быть приостановлена и возобновлена
- yield — для генераторов (простые корутины)
- async/await — для асинхронных корутин (современный стандарт)
- Event loop — система, управляющая выполнением корутин
- Преимущество — множество I/O операций без потоков
- asyncio — стандартная библиотека для работы с корутинами