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

Какие знаешь блокирующие функции при работе с асинхронностью?

2.7 Senior🔥 161 комментариев
#Асинхронность и многопоточность

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

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

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

Какие знаешь блокирующие функции при работе с асинхронностью?

Это один из самых важных вопросов для async разработчика. Блокирующие функции в async коде — это главный источник проблем с производительностью. Расскажу, какие функции блокируют и как их избежать.

Что такое блокирующая функция?

Это функция, которая останавливает весь event loop, пока она выполняется. В это время другие корутины не могут работать.

import asyncio
import time

# ПЛОХО: блокирующая функция в асинхронном коде
async def bad_async_func():
    print("Start")
    time.sleep(5)  # БЛОКИРУЕТ весь event loop на 5 секунд!
    print("End")

# ХОРОШО: асинхронная альтернатива
async def good_async_func():
    print("Start")
    await asyncio.sleep(5)  # Не блокирует, другие корутины работают
    print("End")

Список блокирующих функций

1. time.sleep() — КЛАССИЧЕСКАЯ ОШИБКА

# ПЛОХО
async def bad_delay():
    print("Start:", time.time())
    time.sleep(3)  # Весь event loop остановлен!
    print("End:", time.time())

# ПРАВИЛЬНО
async def good_delay():
    print("Start:", time.time())
    await asyncio.sleep(3)  # Только эта корутина ждёт
    print("End:", time.time())

# Демонстрация
async def main():
    task1 = good_delay()
    task2 = good_delay()
    
    start = time.time()
    await asyncio.gather(task1, task2)
    print(f"Время: {time.time() - start:.1f}s")  # 3s (параллельно!)
    # Если бы использовали time.sleep(), было бы 6s

2. Файловый ввод-вывод (I/O операции)

# ПЛОХО
async def bad_file_read():
    with open('large_file.txt', 'r') as f:  # БЛОКИРУЕТ!
        data = f.read()
    return data

# ПРАВИЛЬНО (используй aiofiles)
import aiofiles

async def good_file_read():
    async with aiofiles.open('large_file.txt', 'r') as f:
        data = await f.read()  # Не блокирует
    return data

3. Сетевые операции

# ПЛОХО (используешь requests)
import requests

async def bad_http_request():
    response = requests.get('https://api.example.com/data')  # БЛОКИРУЕТ!
    return response.json()

# ПРАВИЛЬНО (используй aiohttp или httpx)
import aiohttp

async def good_http_request():
    async with aiohttp.ClientSession() as session:
        async with session.get('https://api.example.com/data') as response:
            return await response.json()  # Не блокирует

# Или httpx
import httpx

async def good_http_request_v2():
    async with httpx.AsyncClient() as client:
        response = await client.get('https://api.example.com/data')
        return response.json()

4. Операции с базой данных

# ПЛОХО (синхронные драйверы)
from sqlalchemy import create_engine

async def bad_db_query():
    engine = create_engine('postgresql://...')
    with engine.connect() as conn:  # БЛОКИРУЕТ!
        result = conn.execute("SELECT * FROM users")
    return result.fetchall()

# ПРАВИЛЬНО (асинхронные драйверы)
import databases  # или asyncpg, aiomysql, motor для MongoDB

database = databases.Database('postgresql://...')

async def good_db_query():
    await database.connect()
    rows = await database.fetch('SELECT * FROM users')  # Не блокирует
    return rows

# Или SQLAlchemy 2.0+ с asyncio
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession

async def good_db_query_v2():
    engine = create_async_engine('postgresql+asyncpg://...')
    async with AsyncSession(engine) as session:
        result = await session.execute('SELECT * FROM users')
        return result.fetchall()

5. Обработка JSON и парсинг

# ПЛОХО
import json

async def bad_json_parse():
    large_json = json.dumps({...})  # Может быть медленно
    data = json.loads(large_json)  # БЛОКИРУЕТ для больших файлов
    return data

# ПРАВИЛЬНО
import orjson  # Быстрый JSON парсер

async def good_json_parse():
    large_json = "{...}"
    # orjson быстрее, или используй loop.run_in_executor()
    data = orjson.loads(large_json)
    return data

6. Интенсивные вычисления (CPU-bound операции)

# ПЛОХО
async def bad_computation():
    result = sum(i**2 for i in range(100_000_000))  # БЛОКИРУЕТ!
    return result

# ПРАВИЛЬНО (используй run_in_executor)
import asyncio
from concurrent.futures import ProcessPoolExecutor

async def good_computation():
    loop = asyncio.get_event_loop()
    result = await loop.run_in_executor(
        ProcessPoolExecutor(),
        lambda: sum(i**2 for i in range(100_000_000))
    )
    return result

7. Модули, которые блокируют

# Блокирующие модули
requests          # HTTP запросы
sqlalchemy        # Синхронный ORM
pyself.psycopg2   # Синхронный PostgreSQL драйвер
mysql.connector   # MySQL
mongo (синхронный)  # MongoDB синхронный
pickle            # Для больших объектов
import subprocess # Запуск процессов
os.system()       # Системные команды
time.sleep()      # Задержки

Правильные асинхронные альтернативы

# Таблица замен

Блокирующие         →  Асинхронные
=====================================
time.sleep()         →  asyncio.sleep()
requests.get()       →  aiohttp / httpx (async)
sqlalchemy (sync)    →  databases / sqlalchemy async
psycopg2             →  asyncpg
mysql.connector      →  aiomysql
open() file I/O      →  aiofiles
subprocess.run()     →  asyncio.create_subprocess_exec()
os.system()          →  asyncio.create_subprocess_shell()
json (большие)       →  orjson
CPU вычисления       →  loop.run_in_executor()

Практический пример: Проверка блокирования

import asyncio
import time

# ПЛОХОЙ КОД
async def slow_task():
    print(f"{time.time()}: Task 1 start")
    time.sleep(2)  # БЛОКИРУЕТ
    print(f"{time.time()}: Task 1 end")

async def slow_task_2():
    print(f"{time.time()}: Task 2 start")
    await asyncio.sleep(2)  # Не блокирует
    print(f"{time.time()}: Task 2 end")

async def main():
    # Запускаем две задачи параллельно
    await asyncio.gather(
        slow_task(),      # Блокирует весь event loop
        slow_task_2()     # Ждёт в очереди
    )

# Результат:
# 1234567890.1: Task 1 start
# 1234567892.1: Task 1 end      <- 2 СЕКУНДЫ!
# 1234567892.1: Task 2 start
# 1234567894.1: Task 2 end
# Общее время: 4 СЕКУНДЫ (последовательно вместо параллельно!)

# ПРАВИЛЬНЫЙ КОД
async def main_correct():
    await asyncio.gather(
        slow_task_2(),    # Не блокирует
        slow_task_2()     # Работает параллельно
    )

# Результат:
# 1234567890.1: Task 2 start
# 1234567890.1: Task 2 start
# 1234567892.1: Task 2 end
# 1234567892.1: Task 2 end
# Общее время: 2 СЕКУНДЫ (параллельно!)

Как найти блокирующий код?

# Инструмент для отладки
import asyncio

class SlowCallback:
    def __init__(self, threshold=0.1):
        self.threshold = threshold
    
    def __call__(self, future):
        if future._loop.time() - future._time > self.threshold:
            print(f"Slow callback: {future._time}")

# Или используй профайлер
import cProfile
import pstats

if __name__ == '__main__':
    profiler = cProfile.Profile()
    profiler.enable()
    asyncio.run(main())
    profiler.disable()
    stats = pstats.Stats(profiler)
    stats.sort_stats('cumulative')
    stats.print_stats(10)  # Топ 10 медленных операций

На собеседовании

"Блокирующие функции в async коде — это операции, которые остановят весь event loop.

Самые частые:

  • time.sleep() → используй asyncio.sleep()
  • requests → используй aiohttp или httpx
  • Синхронные БД драйверы → используй asyncpg, aiomysql, motor
  • Файловый I/O → используй aiofiles
  • Интенсивные вычисления → используй loop.run_in_executor()

Главное правило: если используешь async, ВСЕ операции должны быть асинхронными. Иначе теряется весь смысл асинхронности.

Для поиска: включу await везде, где нужна асинхронность."