← Назад к вопросам
В чём разница между threading и multiprocessing в Python?
2.0 Middle🔥 111 комментариев
#DevOps и инфраструктура#Django
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Решение: Threading vs Multiprocessing в Python
Это один из ключевых вопросов про параллелизм в Python. Threading и multiprocessing решают разные проблемы и имеют совершенно разные характеристики.
Основные Различия
| Аспект | Threading | Multiprocessing |
|---|---|---|
| Изоляция | Общая память | Отдельные процессы |
| GIL | Ограничен GIL (Global Interpreter Lock) | Не ограничен GIL |
| Параллелизм CPU | Нет (только на I/O) | Да (истинный параллелизм) |
| Overhead | Низкий | Высокий |
| Синхронизация | Сложная (использовать Lock, Semaphore) | Проще (separate memory) |
| Shared data | Легко (общая память, но опасно) | Сложно (нужны очереди, pipes) |
| Скорость старта | Быстро | Медленнее |
| Отладка | Сложнее | Проще |
Что такое GIL?
GIL (Global Interpreter Lock) — это мьютекс, который не позволяет нескольким потокам одновременно выполнять Python bytecode.
import threading
import time
def cpu_bound_task():
"""CPU-интенсивная задача"""
total = 0
for i in range(100_000_000):
total += i
return total
# Однопоточное выполнение
start = time.time()
cpu_bound_task()
cpu_bound_task()
serial_time = time.time() - start
print(f"Serial: {serial_time:.2f}s")
# Многопоточное выполнение (из-за GIL будет МЕДЛЕННЕЕ!)
start = time.time()
t1 = threading.Thread(target=cpu_bound_task)
t2 = threading.Thread(target=cpu_bound_task)
t1.start()
t2.start()
t1.join()
t2.join()
thread_time = time.time() - start
print(f"Threading: {thread_time:.2f}s")
Вывод:
Serial: ~10s
Threading: ~12s (медленнее из-за overhead)
Когда Использовать Threading
Threading полезен для I/O-bound операций (сеть, файлы, БД):
import threading
import requests
import time
def download(url):
"""Загрузить одну страницу"""
response = requests.get(url)
print(f"Downloaded {len(response.text)} bytes from {url}")
urls = [
'https://httpbin.org/delay/1',
'https://httpbin.org/delay/1',
'https://httpbin.org/delay/1',
]
# Однопоточно: ~3 секунды
start = time.time()
for url in urls:
download(url)
print(f"Serial: {time.time() - start:.2f}s")
# Многопоточно: ~1 секунда (параллельные I/O)
start = time.time()
threads = []
for url in urls:
t = threading.Thread(target=download, args=(url,))
t.start()
threads.append(t)
for t in threads:
t.join()
print(f"Threading: {time.time() - start:.2f}s")
Когда Использовать Multiprocessing
Multiprocessing нужен для CPU-bound операций (вычисления, обработка данных):
import multiprocessing
import time
def cpu_bound_task(n):
"""CPU-интенсивная задача"""
total = 0
for i in range(n):
total += i
return total
if __name__ == '__main__':
# Однопроцессно: ~10 сек
start = time.time()
cpu_bound_task(100_000_000)
cpu_bound_task(100_000_000)
serial_time = time.time() - start
print(f"Serial: {serial_time:.2f}s")
# Многопроцессно: ~5 сек (на 2-ядерной машине)
start = time.time()
with multiprocessing.Pool(2) as pool:
results = pool.map(cpu_bound_task, [100_000_000, 100_000_000])
parallel_time = time.time() - start
print(f"Multiprocessing: {parallel_time:.2f}s")
Пример: Работа с Данными
Threading для I/O:
import threading
import queue
def producer(q):
"""Производит данные"""
for i in range(5):
time.sleep(0.5) # Имитация I/O
q.put(f"Item {i}")
q.put(None) # Signal to stop
def consumer(q):
"""Потребляет данные"""
while True:
item = q.get()
if item is None:
break
print(f"Consumed: {item}")
q = queue.Queue()
t1 = threading.Thread(target=producer, args=(q,))
t2 = threading.Thread(target=consumer, args=(q,))
t1.start()
t2.start()
t1.join()
t2.join()
Multiprocessing для обработки:
import multiprocessing
def process_data(data):
"""Обработка данных"""
return sum(data) * 2
if __name__ == '__main__':
data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
with multiprocessing.Pool(3) as pool:
results = pool.map(process_data, data)
print(results) # [12, 30, 48]
Синхронизация в Threading
import threading
shared_data = {'count': 0}
lock = threading.Lock()
def increment():
global shared_data
for _ in range(100_000):
with lock: # Защищаем доступ к данным
shared_data['count'] += 1
t1 = threading.Thread(target=increment)
t2 = threading.Thread(target=increment)
t1.start()
t2.start()
t1.join()
t2.join()
print(shared_data['count']) # Всегда 200_000
Синхронизация в Multiprocessing
import multiprocessing
def worker(queue, result):
"""Рабочий процесс"""
value = queue.get()
result.put(value * 2)
if __name__ == '__main__':
queue = multiprocessing.Queue()
result = multiprocessing.Queue()
queue.put(5)
p = multiprocessing.Process(target=worker, args=(queue, result))
p.start()
p.join()
print(result.get()) # 10
Python 3.13+: Удаление GIL
В новых версиях Python работает над удалением GIL (Global Interpreter Lock), что позволит threading работать с истинным параллелизмом.
Таблица Выбора
| Задача | Инструмент | Причина |
|---|---|---|
| Web scraping | Threading | I/O-bound, GIL не блокирует |
| Обработка больших файлов | Multiprocessing | CPU-bound |
| Database queries | Threading | I/O-bound |
| Математические вычисления | Multiprocessing | CPU-bound, обходим GIL |
| Async HTTP requests | asyncio | I/O-bound, без GIL |
| Image processing | Multiprocessing | CPU-bound |
| Telegram bot | asyncio | I/O-bound |
Рекомендация
Для собеседования:
- Threading — для I/O (сеть, файлы, БД). GIL ограничивает CPU, но не блокирует при ожидании I/O.
- Multiprocessing — для CPU-интенсивных задач. Обходит GIL, но больше overhead.
- asyncio — лучший выбор для I/O на одном потоке (особенно web).
- GIL — глобальная блокировка, позволяет только одному потоку выполнять bytecode.
При выборе спроси:
- I/O или CPU-bound?
- Нужна ли общая память?
- Сложность синхронизации?
- Требования к производительности?