← Назад к вопросам
Как работает Event Loop?
3.0 Senior🔥 141 комментариев
#Python Core#Асинхронность и многопоточность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как работает Event Loop
Event Loop (цикл событий) — это сердце асинхронного программирования. Это бесконечный цикл, который управляет выполнением асинхронного кода, переключаясь между ними когда они ждут (например, сеть, файлы).
Базовая идея
┌──────────────────────────────────┐
│ Event Loop (бесконечный цикл) │
├──────────────────────────────────┤
│ 1. Есть ли готовые задачи? │
│ → Выполнить одну │
│ 2. Нет готовых задач? │
│ → Подождать события │
│ 3. Вернуться на шаг 1 │
└──────────────────────────────────┘
Как Python распределяет время
import asyncio
async def task1():
print("Task 1: начало")
await asyncio.sleep(1) # Уступает управление (I/O-bound)
print("Task 1: конец")
async def task2():
print("Task 2: начало")
await asyncio.sleep(0.5) # Уступает управление
print("Task 2: конец")
async def main():
# Запустить обе задачи конкурентно
await asyncio.gather(task1(), task2())
# Выполнение:
# Task 1: начало
# Task 2: начало
# (Event Loop ждёт события от ОС)
# Task 2: конец (через 0.5 сек)
# Task 1: конец (через 1.0 сек)
# Общее время: 1 сек (не 1.5!)
Внутреннее устройство
Event Loop имеет очередь задач (queue) и отслеживает статус каждой:
┌─────────────────────────────────┐
│ Event Loop State │
├─────────────────────────────────┤
│ Ready Queue: │
│ - task_1 (готова к выполнению) │
│ - task_2 (готова к выполнению) │
│ │
│ Waiting Queue: │
│ - task_3 (ждёт I/O) │
│ - task_4 (ждёт таймер) │
└─────────────────────────────────┘
Пошагово: Event Loop в действии
import asyncio
async def fetch_data(url, delay):
print(f"Начало: {url}")
await asyncio.sleep(delay) # Имитация сетевого запроса
print(f"Завершено: {url}")
return f"Данные с {url}"
async def main():
print("Шаг 0: Event Loop начинает")
# Создаём задачи
task1 = asyncio.create_task(fetch_data("api/users", 1))
task2 = asyncio.create_task(fetch_data("api/posts", 0.5))
print("Шаг 1: Обе задачи созданы (готовы к запуску)")
results = await asyncio.gather(task1, task2)
print(f"Шаг 2: Все задачи завершены: {results}")
asyncio.run(main())
# Вывод:
# Шаг 0: Event Loop начинает
# Шаг 1: Обе задачи созданы (готовы к запуску)
# Начало: api/users
# Начало: api/posts
# (Event Loop ждёт события от ОС)
# Завершено: api/posts (0.5 сек прошла)
# Завершено: api/users (1 сек прошла)
# Шаг 2: Все задачи завершены
Event Loop vs Threads
Threads (многопоточность):
Поток 1: ▓▓▓▓ (работает) ░░░ (ждёт) ▓▓▓▓ (работает)
Поток 2: ▓▓▓▓ (работает) ░░░ (ждёт)
Время: ████████ (выполняются параллельно)
Event Loop (асинхронность):
Задача 1: ▓▓▓▓ (работает) ░░░ (ждёт) ▓▓▓▓ (работает)
Задача 2: ▓▓▓▓ (работает) ░░░ (ждёт)
Время: ▓▓▓▓▓▓▓▓▓ (выполняется последовательно, но ждёт не ваш CPU)
Разница: в asyncio ждущие операции не занимают CPU.
Await — как это работает
async def example():
print("До await")
result = await some_async_function() # Уступает управление Event Loop
print("После await")
return result
# Когда выполняется await:
# 1. Текущая задача приостанавливается
# 2. Event Loop берёт другую задачу из очереди
# 3. Выполняет её до следующего await
# 4. Когда async функция завершится, Event Loop вернёт управление
Практический пример: Web Requests
import asyncio
import aiohttp
async def fetch_url(session, url):
async with session.get(url) as response:
return await response.text() # Уступает управление
async def main():
async with aiohttp.ClientSession() as session:
urls = [
'https://api.github.com/users/github',
'https://api.github.com/users/google',
'https://api.github.com/users/microsoft',
]
# 3 сетевых запроса выполняются конкурентно
tasks = [fetch_url(session, url) for url in urls]
results = await asyncio.gather(*tasks)
return results
# Время выполнения:
# - Последовательно: 3 × 0.5 сек = 1.5 сек
# - Параллельно (asyncio): 0.5 сек (макс из всех)
asyncio.run(main())
Event Loop и CPU-bound код
ОПАСНО: если async функция не уступает управление, она блокирует весь Event Loop!
async def bad_example():
# ❌ Это блокирует Event Loop!
total = 0
for i in range(1_000_000_000):
total += i
return total
async def main():
task1 = asyncio.create_task(bad_example())
task2 = asyncio.create_task(asyncio.sleep(0.1))
await asyncio.gather(task1, task2)
# task2 может ждать ДОЛГО, потому что task1 заблокирована
Решение: используй run_in_executor для CPU-bound кода
async def good_example():
loop = asyncio.get_event_loop()
result = await loop.run_in_executor(
None, # Default executor
cpu_bound_function, # Функция
arg1, arg2 # Аргументы
)
return result
Context Manager для Event Loop
async def task():
await asyncio.sleep(1)
return "Done"
# asyncio.run() создаёт Event Loop, запускает задачу, закрывает Loop
asyncio.run(task())
# Или вручную:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
result = loop.run_until_complete(task())
finally:
loop.close()
Отладка Event Loop
import asyncio
# Включить логирование
logging.basicConfig(level=logging.DEBUG)
async def main():
# Получить текущий Event Loop
loop = asyncio.get_event_loop()
print(f"Event Loop: {loop}")
print(f"Running tasks: {asyncio.all_tasks(loop)}")
asyncio.run(main())
Event Loop — фундамент асинхронного программирования в Python, критически важно понимать его поведение для написания эффективного кода.