Многопроцессорное программирование: параллелизм в действии

Почему многопроцессорность? 🚀

Когда ваш скрипт работает медленно, особенно при обработке больших данных или выполнении CPU-bound задач (например, математических вычислений), обычные потоки в Python не помогут из-за GIL (Global Interpreter Lock). Здесь на помощь приходит многопроцессорность — настоящий суперсила Python для параллельных вычислений!

📌 Ключевое отличие от многопоточности:
- Процессы выполняются параллельно (используют разные ядра CPU).
- Потоки выполняются псевдопараллельно (из-за GIL).


Основы модуля multiprocessing 🔧

Стандартная библиотека Python предлагает мощный модуль multiprocessing. Рассмотрим его базовые компоненты:

1. Простой процесс

Создадим и запустим функцию в отдельном процессе:

import multiprocessing

def print_numbers():
    for i in range(5):
        print(f"Число: {i}")

if __name__ == "__main__":
    process = multiprocessing.Process(target=print_numbers)
    process.start()  # Запускаем процесс
    process.join()   # Ждём завершения

🔹 Что важно:
- if __name__ == "__main__": — это обязательно для Windows и MacOS (без этого код может вести себя странно).
- .join() — блокирует основной поток, пока процесс не завершится.


2. Пул процессов 🏊

Если задач много, лучше использовать пул процессов — это группа заранее созданных воркеров, которые распределяют задачи между собой.

import multiprocessing

def square(x):
    return x * x

if __name__ == "__main__":
    with multiprocessing.Pool(processes=4) as pool:  # 4 процесса
        results = pool.map(square, [1, 2, 3, 4, 5])
    print(results)  # [1, 4, 9, 16, 25]

📌 Почему это круто?
- Автоматическое распределение задач.
- Эффективное использование CPU (особенно на многоядерных системах).


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

Процессы не разделяют память (в отличие от потоков). Для обмена данными используются:

1. Очереди (Queue)

Позволяют передавать данные между процессами.

import multiprocessing

def worker(queue):
    queue.put("Привет из дочернего процесса!")

if __name__ == "__main__":
    queue = multiprocessing.Queue()
    p = multiprocessing.Process(target=worker, args=(queue,))
    p.start()
    print(queue.get())  # Получаем сообщение
    p.join()

2. Общая память (Value, Array)

Для числовых данных можно использовать разделяемые переменные.

import multiprocessing

def increment(shared_value):
    shared_value.value += 1

if __name__ == "__main__":
    value = multiprocessing.Value('i', 0)  # 'i' — тип int
    processes = [multiprocessing.Process(target=increment, args=(value,)) 
                 for _ in range(5)]

    for p in processes:
        p.start()
    for p in processes:
        p.join()

    print(value.value)  # Может быть от 1 до 5 (гонка данных!)

⚠️ Важно: Без синхронизации возможна гонка данных (race condition). Для защиты используйте блокировки (Lock).


Когда использовать многопроцессорность? 🎯

  1. CPU-bound задачи (тяжёлые вычисления, ML, обработка изображений).
  2. Параллельная обработка данных (например, загрузка и анализ нескольких файлов).
  3. Когда GIL мешает (в отличие от потоков, процессы независимы).

🚀 Пример из жизни:
Если ваш код обрабатывает 1000 изображений, разбейте их на 4 группы и запустите 4 процесса — ускорение может быть почти в 4 раза (если CPU 4-ядерный)!


Заключение

Многопроцессорность в Python — это мощный инструмент для реального параллелизма. Главное:
- Используйте multiprocessing для CPU-bound задач.
- Помните про Queue, Value, Lock для обмена данными.
- Оптимизируйте количество процессов (обычно Pool(processes=multiprocessing.cpu_count())).

🔥 Для более глубокого погружения смотрите разборы Данилы Бежина на YouTube: https://www.youtube.com/@DanilaBezhin. Вперёд, к параллельным вычислениям!

Скрыть рекламу навсегда

🌱 Индвидидулаьные занятия

Индивидуальные онлайн-занятия по программированию для детей и подростков

Личный подход, без воды, с фокусом на понимание и реальные проекты.

🚀 Записаться на занятие