← Назад к вопросам
Что переключается быстрее - потоки ли процессы в Python?
3.0 Senior🔥 71 комментариев
#Python Core#Асинхронность и многопоточность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Что переключается быстрее - потоки ли процессы в Python?
Ответ: потоки (threads) переключаются быстрее, чем процессы (processes). Но нужно понимать, ЧТО имеется в виду под "быстрее".
Теория: Context switching
Потоки (Threads):
- Существуют в ОДНОМ процессе
- Делят одну память, один heap
- Переключение: быстро (микросекунды)
- Нужно: сохранить регистры и указатель стека
Процессы (Processes):
- Отдельные изолированные программы
- Своя память для каждого
- Переключение: медленнее (миллисекунды)
- Нужно: сохранить всё состояние + переключить virtual memory
Почему потоки быстрее?
import time
import threading
import multiprocessing
# Потоки используют меньше ресурсов
thread = threading.Thread(target=print, args=("Hello",))
print(f"Thread объект: {thread}")
print(f"Memory footprint: ~8KB на поток")
# Процессы — это полные копии Python интерпретатора
process = multiprocessing.Process(target=print, args=("Hello",))
print(f"Process объект: {process}")
print(f"Memory footprint: ~30-50MB на процесс!")
Context switching: бенчмарк
import time
import threading
import multiprocessing
def worker():
"""Простая работа"""
total = 0
for _ in range(1000000):
total += 1
return total
# 1. ПОТОКИ
start = time.time()
threads = []
for _ in range(4):
t = threading.Thread(target=worker)
threads.append(t)
t.start()
for t in threads:
t.join()
threads_time = time.time() - start
print(f"Потоки (4 потока): {threads_time:.2f}s")
# 2. ПРОЦЕССЫ
start = time.time()
processes = []
for _ in range(4):
p = multiprocessing.Process(target=worker)
processes.append(p)
p.start()
for p in processes:
p.join()
processes_time = time.time() - start
print(f"Процессы (4 процесса): {processes_time:.2f}s")
print(f"Потоки быстрее в {processes_time / threads_time:.1f}x раз")
# Результат: потоки примерно в 2-3x раза быстрее
НО ЕСТЬ ПРОБЛЕМА: GIL (Global Interpreter Lock)
Вот в чём подвох Python:
import threading
import time
def cpu_bound_task():
"""Работа требует процессора"""
total = 0
for _ in range(100_000_000):
total += 1
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()
threads_time = time.time() - start
# ПРОЦЕССЫ — работает
from multiprocessing import Process
start = time.time()
p1 = Process(target=cpu_bound_task)
p2 = Process(target=cpu_bound_task)
p1.start()
p2.start()
p1.join()
p2.join()
processes_time = time.time() - start
print(f"Однопоточно: ~8s")
print(f"Потоки (2 потока): ~9-10s (МЕДЛЕННЕЕ!)")
print(f"Процессы (2 процесса): ~4-5s (2x быстрее)")
Вывод: когда что использовать
# 1. I/O-BOUND операции (сетевые запросы, файлы)
# ПОТОКИ лучше! Они переключаются быстро и GIL отпускается
import threading
import requests
import time
def fetch_url(url):
requests.get(url) # блокирующий I/O
start = time.time()
threads = []
for _ in range(10):
t = threading.Thread(target=fetch_url, args=("https://httpbin.org/delay/1",))
threads.append(t)
t.start()
for t in threads:
t.join()
print(f"Потоки для I/O: {time.time() - start:.2f}s") # ~1s (параллельно)
# 2. CPU-BOUND операции (вычисления, обработка данных)
# ПРОЦЕССЫ лучше! GIL не помешает
from multiprocessing import Pool
def calculate(n):
return sum(i**2 for i in range(n))
start = time.time()
with Pool(4) as p:
results = p.map(calculate, [1000000] * 10)
print(f"Процессы для CPU: {time.time() - start:.2f}s") # быстро
# 3. СМЕШАННЫЕ операции
# AsyncIO (асинхронность) — лучший выбор!
import asyncio
import aiohttp
async def fetch_async(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.text()
async def main():
tasks = [fetch_async("https://httpbin.org/delay/1") for _ in range(10)]
await asyncio.gather(*tasks)
start = time.time()
asyncio.run(main())
print(f"AsyncIO: {time.time() - start:.2f}s") # ~1s, без потоков!
Память: потоки vs процессы
import threading
import multiprocessing
import os
print(f"Процесс {os.getpid()} использует ~30MB")
# Создание 100 потоков
threads = []
for _ in range(100):
t = threading.Thread(target=lambda: None)
threads.append(t)
print(f"100 потоков: +~800KB (0.8MB)")
# Создание 100 процессов? Не делай так!
# processes = [multiprocessing.Process(target=lambda: None) for _ in range(100)]
# Это потребует ~3GB памяти!
Сравнительная таблица
┌──────────────────────┬──────────────┬──────────────┐
│ Параметр │ Потоки │ Процессы │
├──────────────────────┼──────────────┼──────────────┤
│ Context switching │ Быстро │ Медленнее │
│ Память на один │ ~8KB │ ~30-50MB │
│ GIL (CPU-bound) │ ПРОБЛЕМА │ No GIL │
│ I/O-bound │ Хорошо │ Хорошо │
│ Data sharing │ Легко │ Сложно (IPC) │
│ Безопасность │ Нужны локи │ Изолированы │
│ Масштабируемость │ До 100s │ До 1000s │
└──────────────────────┴──────────────┴──────────────┘
Практический совет
# 1. I/O-BOUND — используй threading
from concurrent.futures import ThreadPoolExecutor
with ThreadPoolExecutor(max_workers=10) as executor:
results = executor.map(fetch_url, urls)
# 2. CPU-BOUND — используй multiprocessing
from concurrent.futures import ProcessPoolExecutor
with ProcessPoolExecutor(max_workers=4) as executor:
results = executor.map(calculate, data)
# 3. I/O-BOUND (современный способ) — используй asyncio
import asyncio
async def main():
tasks = [fetch_async(url) for url in urls]
return await asyncio.gather(*tasks)
asyncio.run(main())
# 4. Лучший выбор для масштабируемости — async!await
# - Нет GIL
# - Быстрое переключение (даже быстрее потоков)
# - Меньше памяти чем потоки
# - Удобный синтаксис
Резюме
Потоки переключаются быстрее на уровне ОС (миллисекунды vs микросекунды), но из-за GIL в Python:
- Потоки лучше для: I/O-bound (сетевые запросы, файлы, БД)
- Процессы лучше для: CPU-bound вычисления (numpy, scipy, машинное обучение)
- AsyncIO лучше всего для: Современные приложения с множеством I/O операций (веб-сервисы, API клиенты)
Это один из главных недостатков Python, и почему для высоконагруженных систем часто используют Go, Java или Rust вместо Python.