run_in_executor

run_in_executor

KTS

Когда пригодится

Тема сегодняшнего поста будет полезна, если в асинхронном коде вам вдруг позарез понадобилось вызвать синхронную операцию — или выполнить тяжелую cpu bound операцию.


Подробнее о синхронных операциях

Выделяют 2 основных типа синхронных операций: CPU и IO bound.

IO Bound — операция, скорость выполнения которой ограничена скоростью подсистемы ввода-вывода. К таким задачам можно отнести выполнение запросов по сети, операции с базой, чтение/запись файла на диск.

SQL-запрос в базу


CPU Bound — операция, скорость выполнения которой ограничена скоростью CPU.  Задача с вычислением  наборов чисел — например, умножение  матриц.

умножение матриц библиотекой numpy

Вообще, синхронный подход считается традиционным в программировании. Операции при этом полностью блокируют поток других задач на время своего выполнения. Это время может достигать десятков секунд, а то и больше.


Задача

Теперь посмотрим на примеры задач, когда вы программируете асинхронно, но понадобилось вызвать блокирующую синхронную операцию:

IO-операция: нужно подключиться к системе, у которой нет асинхронной библиотеки.

CPU-операция: нужно пройтись по всем ключам большого json в несколько мегабайт, который получили от внешнего сервиса.


Решение

Запускать синхронные io bound или cpu bound операции нужно не в event loop, а в отдельном потоке/процессе.

И вот мы добрались до героя поста: в asyncio есть механизм асинхронного запуска операции в потоке/процессе: run_in_executor.

Его синтаксис можно посмотреть здесь: asyncio.loop.run_in_executor


Что и для какой операции использовать

Для выполнения CPU bound операций стоит использовать run_in_executor c ProcessPoolExecutor. CPU bound операции выполняются в другом процессе и не блокируют event loop.

Для выполнения IO bound операций стоит использовать run_in_executor c ThreadPoolExecutor. Синхронные IO bound операции «отпускают» GIL, и во время выполнения могут выполняться другие потоки — то есть наш event loop.



Report Page