Ответ
Многопроцессорность (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 задач: Для задач, ограниченных вводом/выводом (сеть, диск), асинхронное программирование или многопоточность часто более эффективны из-за меньших накладных расходов.
Ответ 18+ 🔞
А, ну вот, слушай, про многопроцессорность. Это когда ты, блядь, не одного раба заставляешь считать, а целую толпу, и каждый в своей отдельной камере сидит, чтоб не подрались.
Зачем это вообще нужно, спросишь?
А затем, чувак, чтобы задействовать все свои ядра по полной, а не одно ебало, пока остальные в ус не дуют. Для задач, где мозги процессора нужны, а не просто ждать, пока диск скрипнет или сеть ответит. Настоящий параллелизм, ёпта!
А чем это от многопоточности отличается?
Да всем, блядь! Потоки — это как клоуны в одной машине: память одна на всех, дерутся за руль, могут друг другу в тапки нассáть. А процессы — это у каждого своя бронированная тачка, свой бензобак и свой маршрут. Полная изоляция. Один ебнулся — остальные едут дальше, им похуй.
И как это в Питоне работает?
А в Питоне есть модуль multiprocessing. Спасительная штука, чтобы обойти этот ёбаный GIL — такой замок в интерпретаторе, который не даёт потокам работать по-настоящему параллельно. А тут — раз! — каждый процесс свой интерпретатор запускает, и GIL ему не указ. Хуй с горы, а не ограничение.
Смотри, как выглядит эта магия:
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 задачи летят.
- Каждый сам за себя: Своя память, нет гонок данных, один не срет в общий суп.
- Живучесть: Один загнулся — остальные пашут. Не как с потоками — чихнул один, все легли.
Что не очень?
- Тяжеловесность: Запустить процесс — это не поток создать. Памяти жрёт, времени на старт больше.
- Общение — геморрой: Чтобы между этими изолированными упырями данные передать, нужны специальные шлюзы (
Queue,Pipe). Не просто так переменную ткнуть. - Не для всего: Если задача просто ждёт ответа от базы или сети (I/O-bound), то процессы — это как из пушки по воробьям. Тут асинхронность или потоки рулят.