Пул процессов в 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)