Ответ
FastAPI, будучи асинхронным фреймворком, имеет встроенный механизм для корректной обработки синхронных функций-обработчиков (def
вместо async def
).
Принцип работы:
Если FastAPI обнаруживает, что функция-обработчик эндпоинта является стандартной синхронной функцией, он не выполняет ее напрямую в основном цикле событий (event loop). Вместо этого он запускает эту функцию в отдельном пуле потоков (ThreadPoolExecutor
).
Почему это важно?
Это предотвращает блокировку event loop'а. Если бы долгая синхронная операция (например, работа с файлами, обращение к старой библиотеке без async
поддержки) выполнялась в основном потоке, сервер не смог бы обрабатывать другие запросы, пока она не завершится. Вынос в отдельный поток позволяет event loop'у продолжать принимать и обрабатывать другие запросы.
Пример синхронного эндпоинта:
import time
from fastapi import FastAPI
app = FastAPI()
# Эта функция будет выполнена в отдельном потоке
def process_data_sync():
# Имитация долгой, блокирующей операции
time.sleep(2)
return {"status": "done"}
@app.get("/process")
def run_processing():
# FastAPI автоматически вызовет process_data_sync в пуле потоков
result = process_data_sync()
return result
Рекомендации:
- Для I/O-операций (работа с сетью, базами данных) всегда предпочитайте асинхронные функции (
async def
) сawait
, так как это более эффективно и масштабируемо. - Для тяжелых CPU-bound задач (сложные вычисления) этот механизм не является оптимальным. Для таких случаев лучше использовать фоновые задачи (
BackgroundTasks
) или выносить их в отдельные воркеры (например, Celery), чтобы не нагружать потоки, обслуживающие веб-запросы.