Вы всегда можете запустить другой поток вручную, но тогда вы несете ответственность за его работу, например, использование очереди.В Python 3 concurrent.futures
предусмотрен удобный API для выгрузки задач в пул потоков.Метод submit
принимает функцию, передает ее потоку в пуле для ее запуска и немедленно возвращает дескриптор, который предоставит результат (или распространит исключение), когда он будет готов.
run_in_executor
- это привнесение этого удобства в asyncio.Обычно вы не должны запускать блокирующий код внутри asyncio - даже такое простое, как time.sleep()
, запрещено, потому что оно блокирует весь цикл событий.run_in_executor
позволяет вам нарушить это правило.Например:
async def sleep_test():
loop = asyncio.get_event_loop()
print('going to sleep')
await loop.run_in_executor(None, time.sleep, 5)
#time.sleep(5)
print('waking up')
asyncio.run(asyncio.gather(sleep_test(), sleep_test()))
Запуск этого кода показывает, что оба экземпляра сопрограммы спят параллельно.Если бы мы использовали time.sleep()
напрямую, они бы спали последовательно, потому что сон заблокировал бы цикл событий.
Этот пример, конечно, глуп, потому что есть asyncio.sleep()
, который делает всевещь качественно, но она показывает основную идею.Реалистичные варианты использования для run_in_executor
включают:
- выполнение кода с привязкой к ЦП, такого как вычисления с пустым фрагментом, изнутри asyncio
- вызов устаревшего кода, который еще не был перенесен в asyncio
- блокирование вызовов, когда неблокирующие API просто недоступны (драйверы баз данных, доступ к файловой системе)