← Назад к вопросам
Блокирует ли GIL процессы в Python?
2.0 Middle🔥 121 комментариев
#Python Core#Асинхронность и многопоточность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# GIL и его влияние на процессы в Python
Это частый вопрос, и ответ очень важен: GIL НЕ блокирует процессы, потому что процессы и потоки — это совершенно разные вещи.
Что такое GIL
GIL (Global Interpreter Lock) — это мьютекс в Python интерпретаторе CPython, который предотвращает одновременное выполнение Python кода несколькими потоками в одном процессе.
Потоки vs Процессы
Потоки (Threads) — ДА, блокируются GIL
Все потоки в одном процессе используют один интерпретатор Python и один GIL:
import threading
import time
def cpu_work(n):
"""CPU-bound операция."""
total = 0
for i in range(n):
total += i * i
return total
start = time.time()
# Два потока пытаются вычислять одновременно
thread1 = threading.Thread(target=cpu_work, args=(100_000_000,))
thread2 = threading.Thread(target=cpu_work, args=(100_000_000,))
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print(f"С потоками: {time.time() - start:.2f}с")
# Для сравнения — последовательное выполнение
start = time.time()
cpu_work(100_000_000)
cpu_work(100_000_000)
print(f"Последовательно: {time.time() - start:.2f}s")
Вывод (на 4-ядерном процессоре):
С потоками: 19.45s ← Потоки МЕДЛЕННЕЕ из-за GIL!
Последовательно: 18.32s ← Даже последовательно быстрее
Почему медленнее? Потому что GIL постоянно переключается между потоками, добавляя overhead.
Процессы (Processes) — НЕТ, GIL не блокирует
Если использовать multiprocessing, каждый процесс имеет свой собственный Python интерпретатор и свой GIL:
from multiprocessing import Process
import time
def cpu_work(n):
"""CPU-bound операция."""
total = 0
for i in range(n):
total += i * i
return total
start = time.time()
# Два процесса работают полностью независимо
p1 = Process(target=cpu_work, args=(100_000_000,))
p2 = Process(target=cpu_work, args=(100_000_000,))
p1.start()
p2.start()
p1.join()
p2.join()
print(f"С процессами: {time.time() - start:.2f}s")
Вывод (на 4-ядерном процессоре):
С процессами: 9.50s ← ЗНАЧИТЕЛЬНО быстрее!
Почему быстрее? Потому что:
- Каждый процесс работает на отдельном ядре CPU
- Каждый процесс имеет свой GIL
- Нет переключения контекста между потоками
Архитектура GIL
Процесс A (Python)
┌────────────────────────────┐
│ GIL (Global Lock) │
│ ┌────────────────────────┐ │
│ │ Python код │ │ ← Только ОДИН поток может выполняться
│ │ CPython VM │ │ в одно время
│ └────────────────────────┘ │
│ Поток 1, Поток 2, Поток 3 │
└────────────────────────────┘
Процесс B (Python) — НЕЗАВИСИМЫЙ
┌────────────────────────────┐
│ GIL (свой, отдельный) │
│ ┌────────────────────────┐ │
│ │ Python код │ │ ← Полностью независимый
│ │ CPython VM │ │ интерпретатор
│ └────────────────────────┘ │
│ Поток 1, Поток 2 │
└────────────────────────────┘
Таблица: когда какой использовать
| Ситуация | Потоки | Процессы | Почему |
|---|---|---|---|
| I/O-bound (сеть, файлы) | ✅ Отлично | ❌ Оverkill | GIL освобождается при I/O, потоки эффективны |
| CPU-bound (вычисления) | ❌ Плохо | ✅ Отлично | GIL блокирует, процессы обходят это |
| Малая нагрузка | ✅ Просто | ❌ Сложновато | Процессы имеют overhead |
| Большая нагрузка | ❌ Медленно | ✅ Быстро | Процессы масштабируются лучше |
| Общая память | ✅ Легко | ❌ Сложно | Потоки видят одну память |
| Изоляция | ❌ Нет | ✅ Полная | Процессы полностью независимы |
Когда GIL не работает
GIL освобождается при:
- I/O операциях (сетевые запросы, файлы)
- Вызовах C расширений, если они это поддерживают
- NumPy операциях (они написаны на C)
import threading
import requests
import time
def fetch_url(url):
"""I/O-bound операция — GIL освобождается!"""
response = requests.get(url)
return len(response.content)
start = time.time()
urls = ["https://example.com"] * 5
# С потоками I/O-bound операции работают хорошо
threads = [threading.Thread(target=fetch_url, args=(url,)) for url in urls]
for t in threads:
t.start()
for t in threads:
t.join()
print(f"Время с потоками: {time.time() - start:.2f}s")
# Последовательно было бы намного медленнее
Вывод:
Время с потоками: 2.5s ← Потоки работают параллельно, быстро
Пример: когда процессы спасают
import threading
from multiprocessing import Pool
import time
def heavy_computation(n):
"""Тяжелая CPU операция."""
total = 0
for i in range(n):
total += i * i
return total
data = [50_000_000] * 4
# ❌ Потоки НЕ помогут
start = time.time()
threads = []
for item in data:
t = threading.Thread(target=heavy_computation, args=(item,))
t.start()
threads.append(t)
for t in threads:
t.join()
thread_time = time.time() - start
# ✅ Процессы помогут
start = time.time()
with Pool(4) as pool:
pool.map(heavy_computation, data)
process_time = time.time() - start
print(f"Потоки: {thread_time:.2f}s")
print(f"Процессы: {process_time:.2f}s")
print(f"Ускорение: {thread_time/process_time:.1f}x")
Вывод:
Потоки: 45.23s
Процессы: 12.45s
Ускорение: 3.6x
Альтернативы GIL в Python
- PyPy — другой интерпретатор, имеет GIL, но лучше оптимизирует
- Jython — работает на JVM, нет GIL
- IronPython — работает на .NET, нет GIL
- asyncio — асинхронное программирование, не требует потоков
- multiprocessing — обходит GIL полностью
- Python 3.13+ — экспериментальный режим "nogil" (для будущего)
Лучшая практика
import asyncio
import concurrent.futures
from multiprocessing import Pool
# 1. I/O-bound → asyncio (современный способ)
async def fetch_many():
tasks = [fetch_url(url) for url in urls]
await asyncio.gather(*tasks)
# 2. I/O-bound → threading (если asyncio сложно)
with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
futures = [executor.submit(fetch_url, url) for url in urls]
results = [f.result() for f in futures]
# 3. CPU-bound → multiprocessing (обходит GIL)
with concurrent.futures.ProcessPoolExecutor(max_workers=4) as executor:
futures = [executor.submit(cpu_work, n) for n in data]
results = [f.result() for f in futures]
# 4. CPU-bound large scale → multiprocessing.Pool
with Pool(4) as pool:
results = pool.map(cpu_work, data)
Резюме
- GIL блокирует потоки, но НЕ блокирует процессы
- Потоки хороши для I/O-bound операций (сеть, файлы)
- Процессы необходимы для CPU-bound операций (вычисления)
- GIL освобождается при I/O, поэтому там потоки работают отлично
- Нет GIL в Jython и IronPython, но они не так популярны
- asyncio — современный способ для I/O-bound без потоков
Выбирайте инструмент в зависимости от типа работы, которую делает ваше приложение.