Чем multiprocessing отличается от threading?python-41

Основное отличие

Threading использует потоки (threads) внутри одного процесса, а multiprocessing создает отдельные процессы. В Python это имеет критически важное значение из-за GIL (Global Interpreter Lock).

GIL

# GIL предотвращает параллельное выполнение Python-кода в потоках
import threading

counter = 0

def increment():
    global counter
    for _ in range(1000000):
        counter += 1

# Создаем потоки
t1 = threading.Thread(target=increment)
t2 = threading.Thread(target=increment)

t1.start()
t2.start()
t1.join()
t2.join()

print(counter)  # Результат будет меньше 2000000 из-за GIL

Сравнение характеристик

Характеристика threading multiprocessing
Использование CPU Одно ядро (из-за GIL) Множество ядер
Память Общая память Раздельная память
Затраты на создание Низкие Высокие
IPC (Inter-Process Communication) Простой (общая память) Сложный (очереди, pipes)
Отказоустойчивость Ошибка в потоке убивает весь процесс Ошибка в процессе не влияет на другие

Практический пример

import time
import threading
import multiprocessing

def cpu_bound_task(n):
    while n > 0:
        n -= 1

# Threading (неэффективно для CPU-bound задач)
start = time.time()
t1 = threading.Thread(target=cpu_bound_task, args=(10**7,))
t2 = threading.Thread(target=cpu_bound_task, args=(10**7,))
t1.start()
t2.start()
t1.join()
t2.join()
print(f"Threading: {time.time() - start:.2f}s")

# Multiprocessing (эффективно использует CPU)
start = time.time()
p1 = multiprocessing.Process(target=cpu_bound_task, args=(10**7,))
p2 = multiprocessing.Process(target=cpu_bound_task, args=(10**7,))
p1.start()
p2.start()
p1.join()
p2.join()
print(f"Multiprocessing: {time.time() - start:.2f}s")

Когда что использовать

  1. Threading лучше для:

    • I/O-bound задач (сетевые запросы, работа с диском)
    • GUI-приложений (чтобы не блокировать интерфейс)
    • Когда нужен общий доступ к памяти
  2. Multiprocessing лучше для:

    • CPU-bound задач (математические вычисления)
    • Когда нужно использовать несколько ядер CPU
    • Для изоляции критически важных компонентов

Проблемы и решения

Для threading:

  • Race conditions (состояние гонки) → Используйте threading.Lock()
lock = threading.Lock()

def safe_increment():
    global counter
    for _ in range(1000000):
        with lock:
            counter += 1

Для multiprocessing:

  • Сложный обмен данными → Используйте Queue или Pipe
from multiprocessing import Process, Queue

def worker(q):
    q.put("Hello from child!")

q = Queue()
p = Process(target=worker, args=(q,))
p.start()
print(q.get())  # Hello from child!
p.join()

Современные альтернативы

  1. concurrent.futures - высокоуровневый интерфейс
with ThreadPoolExecutor() as executor:   # Для потоков
with ProcessPoolExecutor() as executor:  # Для процессов
  1. asyncio - для I/O-bound задач с одним потоком
  2. joblib или dask - для распределенных вычислений

Резюмируем

выбор между threading и multiprocessing зависит от типа задачи. Threading подходит для I/O-операций и простого параллелизма в рамках одного процесса, тогда как multiprocessing необходим для настоящего параллелизма CPU-операций на многоядерных системах. Всегда учитывайте особенности GIL в Python при проектировании многопоточных приложений.