Многопроцессорность

Многопроцессорность — это подход к параллельному выполнению задач, при котором несколько процессов выполняются одновременно. В отличие от многопоточности, процессы не разделяют память, что делает многопроцессорность эффективной для задач, связанных с вычислениями (CPU-bound).
В Python для работы с процессами используется модуль multiprocessing.

Основные понятия
  • Процесс (Process): Это отдельный экземпляр программы с собственной памятью. Процессы не разделяют память, что позволяет избежать проблем с GIL (Global Interpreter Lock).
  • Использование: Многопроцессорность подходит для CPU-bound задач, таких как вычисления, обработка данных или работа с большими массивами информации.

Создание процессов

Для создания процессов используется класс multiprocessing.Process.

Пример:
import multiprocessing import time def worker(name): print(f"Процесс {name} начал работу") time.sleep(2) # Имитация выполнения задачи print(f"Процесс {name} завершил работу") if __name__ == "__main__": # Создание процессов process1 = multiprocessing.Process(target=worker, args=("A",)) process2 = multiprocessing.Process(target=worker, args=("B",)) # Запуск процессов process1.start() process2.start() # Ожидание завершения процессов process1.join() process2.join() print("Все процессы завершены")
Пояснение:
  • multiprocessing.Process: Создаёт новый процесс. Аргумент target указывает функцию, которую нужно выполнить в процессе, а args — аргументы этой функции.
  • start(): Запускает процесс.
  • join(): Ожидает завершения процесса. Без этого основной процесс программы завершится раньше, чем рабочие процессы.
  • time.sleep(2): Имитирует выполнение задачи, которая занимает 2 секунды.

Обмен данными между процессами

Для обмена данными между процессами используются специальные структуры, такие как multiprocessing.Queue или multiprocessing.Pipe.

Пример с Queue:
import multiprocessing def producer(q): for i in range(5): print(f"Производитель: {i}") q.put(i) def consumer(q): while True: item = q.get() if item is None: break print(f"Потребитель: {item}") if __name__ == "__main__": # Создание очереди q = multiprocessing.Queue() # Создание процессов producer_process = multiprocessing.Process(target=producer, args=(q,)) consumer_process = multiprocessing.Process(target=consumer, args=(q,)) # Запуск процессов producer_process.start() consumer_process.start() # Ожидание завершения производителя producer_process.join() # Сигнал завершения потребителю q.put(None) consumer_process.join()
Пояснение:
  • multiprocessing.Queue: Потокобезопасная очередь для обмена данными между процессами.
  • q.put(i): Добавляет элемент в очередь.
  • q.get(): Извлекает элемент из очереди.
  • q.put(None): Сигнал для завершения работы потребителя.

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

Для выполнения задач в пуле процессов используется multiprocessing.Pool. Это удобно, когда нужно выполнить множество задач параллельно.

Пример:
import multiprocessing def worker(x): return x * x if __name__ == "__main__": with multiprocessing.Pool(4) as pool: results = pool.map(worker, range(10)) print("Результаты:", results)
Пояснение:
  • multiprocessing.Pool: Создаёт пул процессов для параллельного выполнения задач.
  • pool.map(): Применяет функцию worker к каждому элементу списка range(10).
  • Результаты: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Совместное использование памяти

Для совместного использования памяти между процессами используются объекты multiprocessing.Value и multiprocessing.Array.

Пример:
import multiprocessing def increment(counter): for _ in range(100000): counter.value += 1 if __name__ == "__main__": counter = multiprocessing.Value("i", 0) # 'i' означает целое число # Создание процессов process1 = multiprocessing.Process(target=increment, args=(counter,)) process2 = multiprocessing.Process(target=increment, args=(counter,)) # Запуск процессов process1.start() process2.start() # Ожидание завершения процессов process1.join() process2.join() print("Итоговое значение counter:", counter.value)
Пояснение:
  • multiprocessing.Value: Создаёт общую переменную, доступную для всех процессов.
  • counter.value: Доступ к значению переменной.

  • Вывод:
    Итоговое значение counter: 200000

Практические задачи

Задача 1: Многопроцессорный расчёт факториала

Напишите многопроцессорную программу для вычисления факториала чисел.

Решение:
import multiprocessing def factorial(n): result = 1 for i in range(1, n + 1): result *= i return result if __name__ == "__main__": numbers = [5, 10, 15, 20] with multiprocessing.Pool() as pool: results = pool.map(factorial, numbers) print("Факториалы:", results)
Пояснение:
  • multiprocessing.Pool: Создаёт пул процессов для параллельного вычисления факториалов.
  • pool.map(): Применяет функцию factorial к каждому числу в списке.
  • Вывод:
    Факториалы: [120, 3628800, 1307674368000, 2432902008176640000]

Задача 2: Многопроцессорный поиск простых чисел

Напишите многопроцессорную программу для поиска простых чисел в заданном диапазоне.

Решение:
import multiprocessing def is_prime(n): if n < 2: return False for i in range(2, int(n**0.5) + 1): if n % i == 0: return False return True def find_primes(start, end): primes = [] for num in range(start, end): if is_prime(num): primes.append(num) return primes if __name__ == "__main__": ranges = [(1, 100), (100, 200), (200, 300)] with multiprocessing.Pool() as pool: results = pool.starmap(find_primes, ranges) primes = [prime for sublist in results for prime in sublist] print("Простые числа:", primes)
Пояснение:
  • pool.starmap(): Применяет функцию find_primes к каждому диапазону чисел.
  • is_prime(): Проверяет, является ли число простым.
  • Вывод:
    Простые числа: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293]

Рекомендации

  • Используйте многопроцессорность для CPU-bound задач, таких как вычисления или обработка данных.
  • Для обмена данными между процессами используйте multiprocessing.Queue или multiprocessing.Pipe.
  • Для выполнения задач в пуле процессов используйте multiprocessing.Pool.
  • Для совместного использования памяти используйте multiprocessing.Value и multiprocessing.Array.

Сравнение многопоточности и многопроцессорности

Характеристика Многопоточность (Threading) Многопроцессорность (Multiprocessing)
Память Общая память Раздельная память
GIL Есть (ограничивает параллелизм) Нет (полный параллелизм)
Использование I/O-bound задачи CPU-bound задачи
Сложность Проще (общая память) Сложнее (раздельная память)

Многопроцессорность в Python — это мощный инструмент для выполнения задач, связанных с вычислениями. Она позволяет эффективно использовать ресурсы процессора и избежать ограничений GIL. Используйте её для ускорения выполнения CPU-bound задач.


Комментарии

Добавить комментарий

Чтобы оставить комменатрий необходимо Авторизоваться