Для чего предназначен модуль `multiprocessing` в Python и как он работает?

«Для чего предназначен модуль `multiprocessing` в Python и как он работает?» — вопрос из категории Библиотеки и модули, который задают на 10% собеседований Python Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

Ключевые концепции и компоненты:

  • Process: Базовый класс для создания нового процесса.
  • Pool: Предоставляет пул рабочих процессов для выполнения задач параллельно, упрощая распределение и сбор результатов.
  • Механизмы межпроцессного взаимодействия (IPC): Queue (очереди) и Pipe (каналы) используются для безопасного обмена данными между процессами.
  • Синхронизация: Lock, Semaphore, Event для координации работы процессов.

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

from multiprocessing import Process, Queue
import os

def worker(q: Queue, num: int):
    """Функция, выполняемая в отдельном процессе."""
    result = num * 2
    print(f"Процесс {os.getpid()}: обрабатываю {num}, результат {result}")
    q.put(result)

if __name__ == '__main__':
    # Обязательно для запуска процессов на некоторых ОС (например, Windows)
    # Предотвращает рекурсивный импорт и запуск дочерних процессов.
    q = Queue() # Очередь для сбора результатов от процессов
    processes = []
    for i in range(4):
        p = Process(target=worker, args=(q, i))
        processes.append(p)
        p.start() # Запуск процесса

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

    print("nРезультаты из очереди:")
    results = []
    while not q.empty():
        results.append(q.get())
    print(sorted(results)) # Вывод: [0, 2, 4, 6]

Почему multiprocessing важен: Он позволяет эффективно использовать многоядерные процессоры для задач, требующих интенсивных вычислений, значительно ускоряя их выполнение по сравнению с однопоточным подходом.

Важные нюансы:

  • if __name__ == '__main__': Критически важен для корректной работы на Windows и macOS при использовании метода spawn или fork для запуска процессов, чтобы избежать бесконечной рекурсии при создании дочерних процессов.
  • Межпроцессная коммуникация: Обмен данными между процессами (через Queue, Pipe) требует сериализации/десериализации, что может быть медленнее, чем обмен данными между потоками в рамках одного процесса.
  • Выбор между multiprocessing и threading/asyncio:
    • multiprocessing подходит для CPU-bound задач (интенсивные вычисления).
    • threading (многопоточность) и asyncio (асинхронное программирование) лучше подходят для IO-bound задач (работа с сетью, файлами), так как GIL не блокирует операции ввода/вывода.