Threading использует потоки (threads) внутри одного процесса, а multiprocessing создает отдельные процессы. В Python это имеет критически важное значение из-за GIL (Global Interpreter Lock).
# 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")
Threading лучше для:
Multiprocessing лучше для:
Для threading:
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()
with ThreadPoolExecutor() as executor: # Для потоков
with ProcessPoolExecutor() as executor: # Для процессов
выбор между threading и multiprocessing зависит от типа задачи. Threading подходит для I/O-операций и простого параллелизма в рамках одного процесса, тогда как multiprocessing необходим для настоящего параллелизма CPU-операций на многоядерных системах. Всегда учитывайте особенности GIL в Python при проектировании многопоточных приложений.