Event loop — сердце asyncio. Он не запускает код параллельно в нескольких потоках. Он переключается между задачами в момент когда они ждут I/O — и именно за счёт этого достигается конкурентность в одном потоке.
I/O bound vs CPU bound
import time, asyncio
# CPU bound — async не поможет
def sum_squares(n):
return sum(i * i for i in range(n)) # чистые вычисления
# I/O bound — async ускорит
async def fetch(url):
await asyncio.sleep(1) # имитация ожидания сети
Правило: если код ждёт (сеть, диск, база данных) — async даёт выигрыш. Если вычисляет — нет.
Как работает event loop
import asyncio
async def task(name, delay):
print(f"{name}: начал")
await asyncio.sleep(delay) # <-- здесь event loop переключается
print(f"{name}: завершил")
async def main():
# Без gather — последовательно, 3 секунды
await task("A", 1)
await task("B", 2)
asyncio.run(main())
При каждом await event loop смотрит: есть ли другие задачи готовые к выполнению? Если есть — запускает их. Именно поэтому asyncio.gather() работает быстрее последовательных await.
Future и Task
Future — низкоуровневый объект: «обещание» что результат будет в будущем.
Task — обёртка над coroutine, которую event loop выполняет конкурентно.
async def main():
# asyncio.create_task() оборачивает coroutine в Task
# и планирует немедленное выполнение
task1 = asyncio.create_task(task("A", 1))
task2 = asyncio.create_task(task("B", 2))
# Оба task уже запущены. await здесь — просто ожидание завершения
await task1
await task2
Debug режим
import asyncio, logging
logging.basicConfig(level=logging.DEBUG)
# Включает предупреждения о медленных coroutines (> 100ms)
asyncio.run(main(), debug=True)
В debug режиме asyncio предупреждает если coroutine занимает CPU слишком долго — это помогает найти случайно заблокировавший код.
💬 Комментарии (0)
Комментариев пока нет
Станьте первым, кто поделится мнением об этой статье!