Пул процессов в Python

В этой статье вы научитесь использовать класс ProcessPoolExecutor для параллельного выполнения кода, чтобы справиться с задачами, не связанными с процессором.

В предыдущей статье вы узнали, как написать параллельно выполняющийся код, вручную создавая процессы с помощью класса Process из модуля multiprocessing. Но создавать процессы вручную — неэффективно.

Для более эффективного управления процессами можно использовать пул процессов.

Пул процессов — это шаблон для автоматического эффективного управления процессами.

Класс ProcessPoolExecutor из модуля concurrent.futures позволяет создавать и управлять пулом процессов.

Например, класс ProcessPoolExecutor может использовать несколько ядер процессора для создания оптимизированного числа процессов.

ProcessPoolExecutor расширяет класс Executor, у которого есть три метода:

  • submit() — отправляет функцию для выполнения процессом и возвращает объект Future.
  • map() — вызывает функцию для итерируемых объектов.
  • shutdown() — завершает работу исполнителя.

Чтобы освободить ресурсы, удерживаемые исполнителем, необходимо явно вызвать метод shutdown(). Для автоматического отключения исполнителя можно использовать менеджер контекста.

Объект Future представляет собой результат асинхронной операции. У него есть два основных метода для получения результата:

  • result() — возвращает результат асинхронной операции.
  • exception() — возвращает исключение, возникшее во время выполнения асинхронной операции.

Пример использования ProcessPoolExecutor

В следующей программе используется пул процессов для создания миниатюр изображений из папки images и сохранения их в папке thumbs.

Примечание. Убедитесь, что у вас установлена библиотека для обработки изображений Pillow. Установить библиотеку можно так: pip install Pillow.

import time
import os
from concurrent.futures import ProcessPoolExecutor
from PIL import Image, ImageFilter

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'):
    img = Image.open(filename)
    img = img.filter(ImageFilter.GaussianBlur())
    img.thumbnail(size)
    img.save(f'{thumb_dir}/{os.path.basename(filename)}')
    print(f'Файл {filename} обработан...')


if __name__ == '__main__':
    start = time.perf_counter()

    with ProcessPoolExecutor() as executor:
        executor.map(create_thumbnail, filenames)
   
    finish = time.perf_counter()

    print(f'Выполнение заняло {finish-start: .2f} секунд.')

Вывод

Файл image/5.jpg обработан
Файл image/5.jpg обработан
Файл image/5.jpg обработан
Файл image/5.jpg обработан
Файл image/5.jpg обработан
Выполнение заняло 0.79 секунд.

Как это работает

1. Объявляем список файлов для создания миниатюр:

filenames = [
    'images/1.jpg',
    'images/2.jpg',
    'images/3.jpg',
    'images/4.jpg',
    'images/5.jpg',
]

2. Создаем функцию, которая будет делать из изображения миниатюру и сохранять результат в папке thumbs:

def create_thumbnail(filename, size=(50,50),thumb_dir ='thumbs'):
    img = Image.open(filename)
    img = img.filter(ImageFilter.GaussianBlur())
    img.thumbnail(size)
    img.save(f'{thumb_dir}/{os.path.basename(filename)}')
    print(f'Файл {filename} обработан...')

3. Создаем пул процессов и вызываем функцию create_thumbnail() для каждого изображения, указанного в списке filenames:

with ProcessPoolExecutor() as executor:
    executor.map(create_thumbnail, filenames)
codechick

СodeСhick.io - простой и эффективный способ изучения программирования.

2024 ©