Вопрос проверяет понимание механизма run_in_executor в AsyncIO, который позволяет выполнять блокирующий код в отдельных потоках или процессах, не блокируя основной цикл событий.
В асинхронном программировании на Python с использованием asyncio основная идея — не блокировать цикл событий (event loop). Однако иногда приходится работать с библиотеками, которые выполняют блокирующие операции (например, тяжёлые вычисления, синхронные вызовы к базам данных или файловые операции). Для таких случаев в asyncio предусмотрен метод run_in_executor.
Метод loop.run_in_executor(executor, func, *args) принимает исполнителя (executor), функцию и её аргументы. Исполнитель — это объект, реализующий интерфейс concurrent.futures.Executor, например ThreadPoolExecutor (по умолчанию) или ProcessPoolExecutor. Метод помещает функцию в очередь исполнителя и возвращает объект asyncio.Future, который можно ожидать с помощью await. Пока функция выполняется в отдельном потоке/процессе, цикл событий продолжает обрабатывать другие задачи.
Допустим, у нас есть синхронная функция, которая имитирует долгую операцию:
import asyncio
import time
def blocking_task(seconds: int) -> str:
time.sleep(seconds) # Блокирующий вызов
return f"Завершено через {seconds} секунд"
async def main():
loop = asyncio.get_running_loop()
# Запускаем блокирующую функцию в потоковом исполнителе
future = loop.run_in_executor(None, blocking_task, 2)
print("Ожидаем результат...")
result = await future
print(result)
asyncio.run(main())Вывод: Используйте run_in_executor, когда вам нужно выполнить блокирующий или ресурсоёмкий код внутри асинхронного приложения, не нарушая работу цикла событий. Это особенно полезно для постепенной миграции с синхронного кода на асинхронный или для работы со сторонними синхронными библиотеками.