Ответ
Да, это возможно, но требует правильного подхода, чтобы не блокировать цикл событий (event loop), что является главной проблемой при смешивании синхронного и асинхронного кода.
Существует два основных сценария:
1. Для быстрых, неблокирующих операций (CPU-bound)
Если синхронная функция выполняется быстро и не ожидает операций ввода-вывода (I/O), ее можно вызывать напрямую. Это не нанесет вреда производительности.
import asyncio
def fast_sync_operation(a, b):
# Быстрые вычисления без ожидания
return a + b
async def main():
result = fast_sync_operation(5, 10)
print(f"Результат: {result}")
await asyncio.sleep(1) # Другая асинхронная работа
asyncio.run(main())
2. Для долгих, блокирующих операций (I/O-bound)
Если функция выполняет долгие операции (например, работа с файлами, запросы к БД через синхронную библиотеку, тяжелые вычисления), ее нужно запускать в отдельном потоке, чтобы не замораживать event loop. Для этого используется loop.run_in_executor().
import asyncio
import time
def slow_blocking_io():
print("Начало долгой операции...")
time.sleep(2) # Имитация блокирующей операции (например, чтение файла)
print("...долгая операция завершена")
return "Данные получены"
async def main():
loop = asyncio.get_running_loop()
print("Запускаем блокирующую задачу без остановки event loop")
# Запуск синхронной функции в пуле потоков по умолчанию
result = await loop.run_in_executor(None, slow_blocking_io)
print(f"Результат из executor: {result}")
asyncio.run(main())
Ключевое правило: Никогда не вызывайте блокирующие I/O операции напрямую в асинхронном коде. Всегда используйте run_in_executor или ищите асинхронные аналоги библиотек.
Ответ 18+ 🔞
Ага, слушай, вот тебе на самом деле интересный вопрос, как эту синхронную хрень в асинхронный код запихнуть, чтобы всё не накрылось медным тазом. Главная засада — это твой event loop, который должен крутиться, как белка в колесе, а не вставать колом, пока какая-нибудь синхронная функция ебёт мозг процессору.
Так, смотри, есть два основных пути, куда свернуть.
Первый — если операция быстрая и не ебёт I/O.
Ну, типа сложить два числа или там строку преобразовать. Если она мгновенная, то вызывай её прямо так, похуй. Цикл событий даже не успеет чихнуть.
import asyncio
def fast_sync_operation(a, b):
# Быстрые вычисления без ожидания
return a + b
async def main():
result = fast_sync_operation(5, 10)
print(f"Результат: {result}")
await asyncio.sleep(1) # Другая асинхронная работа
asyncio.run(main())
Вот тут всё просто, блядь. Никакой магии.
А вот второй сценарий — это когда у тебя операция долгая, блокирующая, ёпта.
Ну, представь: чтение здоровенного файла, запрос к какой-нибудь старой библиотеке для БД, которая только синхронная, или там тяжёлые вычисления. Если ты её вызовешь прямо в корутине — всё, пидарас, event loop встанет, как вкопанный, и все остальные задачи будут ждать, пока этот урод закончит. Волнение ебать!
Для такого дела надо юзать loop.run_in_executor(). Он эту синхронную херню запустит в отдельном потоке, и твой главный цикл сможет дальше спокойно жить своей жизнью.
import asyncio
import time
def slow_blocking_io():
print("Начало долгой операции...")
time.sleep(2) # Имитация блокирующей операции (например, чтение файла)
print("...долгая операция завершена")
return "Данные получены"
async def main():
loop = asyncio.get_running_loop()
print("Запускаем блокирующую задачу без остановки event loop")
# Запуск синхронной функции в пуле потоков по умолчанию
result = await loop.run_in_executor(None, slow_blocking_io)
print(f"Результат из executor: {result}")
asyncio.run(main())
Видишь? Всё честно. Сказали "запусти это где-нибудь в сторонке" и спокойно ждём результата с await.
И главное правило, которое надо выжечь себе на подкорке, блядь: никогда не вызывай блокирующие I/O операции напрямую в асинхронном коде. Никогда! Либо через run_in_executor прогоняй, либо ищи асинхронные аналоги библиотек. Иначе получишь performance пиздец, и все будут тебя ненавидеть. Всё.