Что такое многопроцессорность (multiprocessing) в Python?

Ответ

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

Цель многопроцессорности:

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

Отличие от многопоточности:

В отличие от потоков (threads), которые выполняются в рамках одного процесса и разделяют его память, процессы полностью изолированы. Это обеспечивает:

  • Изоляцию памяти: Отсутствие общих данных по умолчанию исключает проблемы с состоянием и race conditions, характерные для многопоточности.
  • Устойчивость: Сбой одного процесса не приводит к краху всей программы, так как другие процессы продолжают работать независимо.

Многопроцессорность в Python:

В Python модуль multiprocessing позволяет создавать и управлять процессами. Он является ключевым для обхода ограничения Global Interpreter Lock (GIL), которое препятствует истинному параллелизму в многопоточных приложениях на одном интерпретаторе Python. Каждый процесс запускает свой собственный интерпретатор Python, тем самым обходя GIL.

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

from multiprocessing import Process, current_process
import os
import time

def worker(name: str):
    """Функция, выполняемая в отдельном процессе."""
    print(f"Процесс {name} (PID: {os.getpid()}) начал работу.")
    time.sleep(2) # Имитация выполнения CPU-bound задачи
    print(f"Процесс {name} (PID: {os.getpid()}) завершил работу.")

if __name__ == '__main__':
    print(f"Главный процесс (PID: {os.getpid()}) начал работу.")
    processes = []
    for i in range(3):
        # Создаем новый процесс, указывая целевую функцию и аргументы
        p = Process(target=worker, args=(f'Worker-{i}',))
        processes.append(p)
        p.start() # Запускаем процесс

    # Ожидаем завершения всех дочерних процессов
    for p in processes:
        p.join()

    print(f"Главный процесс (PID: {os.getpid()}) завершил работу.")

Преимущества:

  • Истинный параллелизм: Эффективное использование всех доступных ядер процессора для CPU-bound задач.
  • Изоляция: Каждый процесс имеет свою память, что исключает проблемы с состоянием и race conditions.
  • Устойчивость: Сбой одного процесса не влияет на другие, повышая общую надежность системы.

Недостатки:

  • Накладные расходы: Создание и управление процессами требует больше ресурсов (памяти, времени CPU) по сравнению с потоками.
  • Обмен данными: Обмен данными между процессами сложнее и требует специальных механизмов (например, Queue, Pipe, Value, Array из модуля multiprocessing).
  • Не подходит для I/O-bound задач: Для задач, ограниченных вводом/выводом (сеть, диск), асинхронное программирование или многопоточность часто более эффективны из-за меньших накладных расходов.