Гайды

Использование asyncio в Python

Event loop, Task и gather, таймауты и отмена, Lock и Semaphore, run_in_executor и отладка.

~10 мин чтения

Использование asyncio в Python

asyncio — стандартная библиотека кооперативной многозадачности в одном потоке (event loop): async/await, Tasks, синхронизация, стримы. В вебе это основа ASGI (FastAPI). Связка с API — Асинхронные эндпоинты и asyncpg; не путать с потоками Celery — Celery: очереди.


1. Корутина и цикл событий

python
import asyncio

async def main():
    await asyncio.sleep(0.1)
    return "done"

asyncio.run(main())

asyncio.run() — создаёт loop, запускает корутину, закрывает loop (точка входа в скриптах).


2. Task и gather

python
async def fetch(n: int) -> int:
    await asyncio.sleep(0.05)
    return n

async def main():
    tasks = [asyncio.create_task(fetch(i)) for i in range(5)]
    results = await asyncio.gather(*tasks)
    return results

create_task — планирование; gather — параллельное ожидание (с return_exceptions=True при частичных ошибках).


3. Таймауты и отмена

python
async with asyncio.timeout(5):
    await slow_call()

# или
task = asyncio.create_task(slow_call())
task.cancel()
try:
    await task
except asyncio.CancelledError:
    ...

4. Синхронизация

  • Lock — взаимное исключение.
  • Semaphore — ограничение параллелизма (например, не больше 10 одновременных HTTP-запросов).
  • Queue — producer-consumer внутри процесса.

5. run_in_executor

Блокирующий код (CPU или старый sync I/O) не кладите в async def без изоляции:

python
loop = asyncio.get_running_loop()
await loop.run_in_executor(None, blocking_read_file, path)

6. Стримы: StreamReader/Writer

Низкоуровневые TCP/протоколы; для HTTP клиентов проще httpx.AsyncClient.


7. Отладка

  • asyncio.get_event_loop().set_debug(True) в dev — предупреждения о медленных колбэках.
  • Не смешивать разные loop в одном потоке.

asyncio.wait_for(coro, timeout) — оборачивает любую корутину дедлайном (удобнее, чем вручную create_task + cancel для простых случаев). Для совместимости с библиотеками вроде AnyIO (используется в Starlette/FastAPI) те же идеи таймаутов и отмены применимы к группам задач.


8. Чек-лист

  • Нет скрытых time.sleep в async-коде — только asyncio.sleep.
  • Ограничение параллелизма внешних API через Semaphore.
  • Явные таймауты на сетевые вызовы.
  • Тяжёлая CPU-работа — в процессах (ProcessPoolExecutor), не в loop.

Дальше: WebSockets в FastAPI · тег Python