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

Можно ли в асинхронной функции использовать синхронные функции?

2.2 Middle🔥 141 комментариев
#Python Core#Асинхронность и многопоточность

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

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

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

Использование синхронных функций в асинхронных функциях

Да, можно использовать синхронные функции внутри асинхронных функций, но это требует понимания нюансов и потенциальных проблем с производительностью. Этот вопрос часто встречается при работе с asyncio и асинхронным Python кодом.

Простой случай: лёгкие синхронные операции

Если синхронная функция выполняется быстро (обработка данных, вычисления), её можно вызвать напрямую:

import asyncio

# Синхронная функция
def calculate_sum(a: int, b: int) -> int:
    """Простые вычисления"""
    return a + b

# Асинхронная функция, которая вызывает синхронную
async def process_data(x: int, y: int) -> int:
    result = calculate_sum(x, y)  # Синхронный вызов
    print(f"Результат: {result}")
    return result

# Использование
async def main():
    result = await process_data(5, 3)
    print(result)  # 8

asyncio.run(main())

Это работает потому, что синхронная функция выполняется так быстро, что не блокирует event loop.

Проблема: блокирующие синхронные операции

Проблема возникает, когда синхронная функция выполняет блокирующую операцию (I/O операции, CPU-интенсивные задачи). Это блокирует весь event loop и приостанавливает выполнение других асинхронных задач:

import asyncio
import time

# Блокирующая синхронная функция
def blocking_operation():
    """Это заблокирует event loop на 2 секунды"""
    time.sleep(2)  # Блокирующее ожидание
    return "Готово"

# Неправильный способ
async def bad_approach():
    result = blocking_operation()  # ❌ Блокирует весь event loop!
    return result

# Правильный способ 1: loop.run_in_executor()
async def good_approach_1():
    loop = asyncio.get_event_loop()
    # Выполняем синхронную функцию в отдельном потоке
    result = await loop.run_in_executor(None, blocking_operation)
    return result

# Правильный способ 2: asyncio.to_thread (Python 3.9+)
async def good_approach_2():
    result = await asyncio.to_thread(blocking_operation)
    return result

async def main():
    print("Начало")
    result = await good_approach_2()
    print(result)  # "Готово"

asyncio.run(main())

Практический пример: работа с файлами и БД

import asyncio
import time
from typing import List

# Синхронная функция для работы с файлом
def read_file_sync(filename: str) -> str:
    """Синхронное чтение файла (блокирующая операция)"""
    with open(filename, r) as f:
        return f.read()

# Синхронная функция для работы с БД
def query_database_sync(query: str) -> List[dict]:
    """Синхронный запрос к БД (блокирующая операция)"""
    time.sleep(1)  # Имитируем задержку БД
    return [{"id": 1, "name": "Alice"}]

# Асинхронная обёртка с executor
async def fetch_data_async():
    loop = asyncio.get_event_loop()
    
    # Выполняем обе блокирующие операции параллельно
    file_content, db_result = await asyncio.gather(
        loop.run_in_executor(None, read_file_sync, "data.txt"),
        loop.run_in_executor(None, query_database_sync, "SELECT * FROM users")
    )
    
    return {"file": file_content, "db": db_result}

async def main():
    result = await fetch_data_async()
    print(result)

asyncio.run(main())

CPU-интенсивные операции

Для CPU-интенсивных задач используйте ProcessPoolExecutor вместо ThreadPoolExecutor:

import asyncio
from concurrent.futures import ProcessPoolExecutor

def cpu_intensive_task(n: int) -> int:
    """CPU-интенсивное вычисление"""
    return sum(i * i for i in range(n))

async def process_intensive():
    loop = asyncio.get_event_loop()
    # Используем ProcessPoolExecutor для CPU-задач
    result = await loop.run_in_executor(
        ProcessPoolExecutor(),
        cpu_intensive_task,
        1000000
    )
    return result

asyncio.run(process_intensive())

Сравнение методов

СценарийМетодПример
Лёгкие вычисленияПрямой вызовx = sync_func()
I/O блокирующиеrun_in_executor()await loop.run_in_executor(None, func)
I/O блокирующие (3.9+)asyncio.to_thread()await asyncio.to_thread(func)
CPU-интенсивныеProcessPoolExecutorawait loop.run_in_executor(pool, func)

Чек-лист правильного использования

  1. ✅ Быстрые синхронные функции (вычисления) → вызывайте напрямую
  2. ❌ НЕ вызывайте блокирующие операции без executor
  3. ✅ Используйте asyncio.to_thread() для I/O в Python 3.9+
  4. ✅ Используйте run_in_executor() для универсальности
  5. ✅ Запускайте CPU-задачи в отдельных процессах

Вывод

Синхронные функции в асинхронных функциях допустимы и часто необходимы, но нужно быть осторожным с блокирующими операциями. Ключ — использовать executor (поток или процесс) для операций, которые блокируют I/O, чтобы не заморозить event loop и не потерять преимущества асинхронного программирования.