ProcessPoolExecutor в Python на практических примерах
В этом руководстве вы узнаете, как использовать ProcessPoolExecutor в Python для эффективного создания пула процессов и управления им на практических примерах.
Что такое класс ProcessPoolExecutor в Python?
Ранее вы узнали, как запускать код параллельно, создавая процессы вручную с помощью класса Process из модуля многопроцессорности. Однако создавать процессы вручную не всегда эффективно.
Чтобы управлять процессами более эффективно, вы можете использовать пул процессов. Подобно пулу потоков, пул процессов представляет собой шаблон автоматического управления процессами.
Класс ProcessPoolExecutor из модуля concurrent.futures в Python позволяет создавать пул процессов и управлять им. Например, класс ProcessPoolExecutor использует количество ядер ЦП для создания оптимизированного количества создаваемых процессов.
ProcessPoolExecutor расширяет класс Executor, который имеет три метода:
- submit() – отправляет функцию, которая будет выполнена процессом, и возвращает объект Future.
- map() – вызов функции для итерации элементов.
- Shutdown() – закрыть исполнителя.
Чтобы освободить ресурсы, удерживаемые исполнителем, вам необходимо явно вызвать метод Shutdown(). Для автоматического закрытия исполнителя можно использовать контекстный менеджер.
Объект Future представляет собой насыщенный событиями результат асинхронной операции. Он имеет два основных метода получения результата:
- result() – возвращает результат асинхронной операции.
- exception() — возвращает исключение, возникшее во время выполнения асинхронной операции.
Пример с ProcessPoolExecutor
Следующая программа использует пул процессов для создания миниатюр изображений в папке изображений и сохранения их в папке миниатюр.
import time import os from PIL import Image, ImageFilter from concurrent.futures import ProcessPoolExecutor filenames = [ 'images/1.jpg', 'images/2.jpg', 'images/3.jpg', 'images/4.jpg', 'images/5.jpg', ] def create_thumbnail(filename, size=(50,50), thumb_dir ='thumbs'): # open the image img = Image.open(filename) # apply the gaussian blur filter img = img.filter(ImageFilter.GaussianBlur()) # create a thumbnail img.thumbnail(size) # save the image img.save(f'{thumb_dir}/{os.path.basename(filename)}') # display a message print(f'{filename} was processed...') def main(): start = time.perf_counter() with ProcessPoolExecutor() as executor: executor.map(create_thumbnail, filenames) finish = time.perf_counter() print(f'It took {finish-start: .2f} second(s) to finish') if __name__ == '__main__': main()
Выход:
images/5.jpg was processed... images/4.jpg was processed... images/3.jpg was processed... images/2.jpg was processed... images/1.jpg was processed... It took 0.79 second(s) to finish
Обратите внимание, что для запуска программы вам необходимо установить Pillow — популярную библиотеку для обработки изображений, выполнив pip команду — pip install Pillow.
Как это работает.
- Сначала объявим список файлов для создания миниатюр:
filenames = [ 'images/1.jpg', 'images/2.jpg', 'images/3.jpg', 'images/4.jpg', 'images/5.jpg', ]
- Во-вторых, определим функцию, которая создает миниатюру из файла изображения и сохраняет выходные данные в папке миниатюр:
def create_thumbnail(filename, size=(50,50), thumb_dir ='thumbs'): # open the image img = Image.open(filename) # apply the gaussian blur filter img = img.filter(ImageFilter.GaussianBlur()) # create a thumbnail img.thumbnail(size) # save the image img.save(f'{thumb_dir}/{os.path.basename(filename)}') # display a message print(f'{filename} was processed...')
- В-третьих, создадим пул процессов и вызовем функцию create_thumbnail() для каждого изображения, указанного в именах файлов:
with ProcessPoolExecutor() as executor: executor.map(create_thumbnail, filenames)