← Назад к вопросам
Для чего нужно отключать GIL?
3.0 Senior🔥 111 комментариев
#Python Core#Асинхронность и многопоточность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Для чего нужно отключать GIL?
GIL (Global Interpreter Lock) — это мьютекс (блокировка) в CPython, которая позволяет только ОДНОМУ потоку выполнять Python код одновременно. Отключение GIL нужно для достижения настоящего параллелизма в многопоточных приложениях.
Что такое GIL?
# GIL — это глобальная блокировка
# Даже на многоядерном процессоре:
import threading
def cpu_intensive():
total = 0
for i in range(500_000_000):
total += i
return total
thread1 = threading.Thread(target=cpu_intensive)
thread2 = threading.Thread(target=cpu_intensive)
import time
start = time.time()
# Запускаем два потока одновременно
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print(f"Два потока: {time.time() - start:.2f}s")
# Результат: примерно 10+ секунд (как один поток!)
# Потоки не работают параллельно из-за GIL
# А если однопоточно?
start = time.time()
cpu_intensive()
cpu_intensive()
print(f"Один поток: {time.time() - start:.2f}s")
# Результат: примерно 10 секунд (примерно то же!)
Почему GIL существует?
1. УПРАВЛЕНИЕ ПАМЯТЬЮ
├─ CPython использует reference counting
├─ Счётчик ссылок НЕ потокобезопасен
└─ Нужна глобальная блокировка
2. СОВМЕСТИМОСТЬ
├─ Много C расширений рассчитаны на GIL
├─ Убрать GIL = сломать совместимость
└─ Это был компромисс
3. ПРОИЗВОДИТЕЛЬНОСТЬ В ОДНОПОТОЧНОСТИ
├─ GIL делает однопоточные приложения быстрее
├─ Нет контенции на лок
└─ Чистая выигрыш для большинства кода
Когда GIL мешает
# 1. CPU-BOUND многопоточность
import threading
import time
def calculate():
return sum(i**2 for i in range(10_000_000))
start = time.time()
# ПЛОХО: два потока, но выполняются по очереди
threads = [threading.Thread(target=calculate) for _ in range(2)]
for t in threads:
t.start()
for t in threads:
t.join()
threads_time = time.time() - start
print(f"2 потока (CPU-bound): {threads_time:.2f}s")
# ХОРОШО: процессы НЕ имеют GIL
from multiprocessing import Process
start = time.time()
processes = [Process(target=calculate) for _ in range(2)]
for p in processes:
p.start()
for p in processes:
p.join()
processes_time = time.time() - start
print(f"2 процесса (CPU-bound): {processes_time:.2f}s")
print(f"Процессы быстрее в {threads_time / processes_time:.1f}x раз")
Почему NOT нужно отключать GIL (при I/O)
# Для I/O операций GIL ОТПУСКАЕТСЯ!
import threading
import time
import requests
def fetch_url():
# Во время network I/O GIL отпускается
requests.get("https://httpbin.org/delay/1")
start = time.time()
threads = [threading.Thread(target=fetch_url) for _ in range(10)]
for t in threads:
t.start()
for t in threads:
t.join()
print(f"10 потоков для I/O: {time.time() - start:.2f}s")
# Результат: примерно 1-2 секунды (параллельно!)
# GIL отпустился во время блокирующего I/O
PEP 703: Отключение GIL
Сэм Гросс предложил в 2023 году отключаемый GIL:
# Python 3.13+: --disable-gil флаг
# python --disable-gil script.py
import threading
import time
def cpu_bound():
return sum(i**2 for i in range(100_000_000))
start = time.time()
threads = [threading.Thread(target=cpu_bound) for _ in range(4)]
for t in threads:
t.start()
for t in threads:
t.join()
print(f"4 потока (с GIL): 20+ секунд")
print(f"4 потока (без GIL): 5-6 секунд")
# С отключённым GIL потоки работают ПАРАЛЛЕЛЬНО!
Как Python отпускает GIL?
# 1. Во время блокирующего I/O
import socket
def network_io():
sock = socket.socket()
# GIL отпускается здесь!
sock.connect(("example.com", 80))
# GIL захватывается обратно
# 2. Функции NumPy работают БЕЗ GIL
import numpy as np
array = np.arange(10_000_000)
result = array.sum() # GIL отпущен!
# 3. В C расширениях можно явно отпустить
# Py_BEGIN_ALLOW_THREADS
// C code
// Py_END_ALLOW_THREADS
Решения вместо отключения GIL
# 1. MULTIPROCESSING — для CPU-bound
from multiprocessing import Pool
def heavy_calculation(n):
return sum(i**2 for i in range(n))
with Pool(4) as pool:
results = pool.map(heavy_calculation, [10_000_000] * 4)
# Работает параллельно, нет GIL
# 2. ASYNCIO — для I/O-bound (лучший выбор!)
import asyncio
import aiohttp
async def fetch_many():
async with aiohttp.ClientSession() as session:
tasks = []
for url in urls:
tasks.append(session.get(url))
return await asyncio.gather(*tasks)
asyncio.run(fetch_many())
# Один поток, но тысячи параллельных I/O операций
# 3. NUMPY/PANDAS — для вычислений
import numpy as np
import pandas as pd
data = np.random.randn(10_000_000)
result = data.mean() # GIL отпущен, работает быстро
df = pd.DataFrame({"col": range(10_000_000)})
df["squared"] = df["col"] ** 2 # Параллельно!
# 4. JYTHON или IRONPYTHON — альтернативные реализации
# Jython (Java) и IronPython (.NET) НЕ имеют GIL!
# Но они медленнее CPython в однопоточности
Сравнение подходов
# Задача: обработать 4 файла по 1GB каждый
# ❌ НЕПРАВИЛЬНО: потоки с GIL
import threading
threads = [threading.Thread(target=process_file, args=(f,)) for f in files]
for t in threads: t.start()
for t in threads: t.join()
# Медленно! Потоки по очереди
# ✅ ПРАВИЛЬНО: процессы
from multiprocessing import Pool
with Pool(4) as pool:
pool.map(process_file, files)
# Быстро! Истинный параллелизм
# ✅ ПРАВИЛЬНО: async для I/O
import asyncio
async def async_process_files():
tasks = [async_process_file(f) for f in files]
await asyncio.gather(*tasks)
asyncio.run(async_process_files())
# Один процесс, много I/O параллельно
Python 3.13: Экспериментальное отключение GIL
# Установка с отключённым GIL
python3.13 --disable-gil script.py
# Или компиляция из исходников
./configure --disable-gil
make
Performance с отключённым GIL
# На Python 3.13 с --disable-gil
import threading
import time
def cpu_bound():
return sum(i**2 for i in range(100_000_000))
start = time.time()
threads = [threading.Thread(target=cpu_bound) for _ in range(8)]
for t in threads: t.start()
for t in threads: t.join()
with_gil = time.time() - start # ~30 сек
# python3.13 --disable-gil script.py
# без GIL: ~4 сек (8x ускорение)
Резюме
GIL нужно отключать когда:
- Много CPU-bound операций в потоках
- Нужен истинный параллелизм на многоядерном процессоре
- Используется Python 3.13+
GIL НЕ мешает когда:
- I/O-bound операции (сетевые запросы, файлы, БД)
- NumPy/Pandas вычисления
- AsyncIO приложения
Лучшие практики прямо сейчас:
- Используй
asyncioдля I/O-bound - Используй
multiprocessingдля CPU-bound - Используй NumPy/Pandas для вычислений (GIL отпускается)
- Жди Python 3.13+ и используй
--disable-gil