← Назад к вопросам
Почему GIL не позволяет реализовать настоящую многопоточность (multithreading) в Python?
1.0 Junior🔥 161 комментариев
#Асинхронность и многопоточность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Ответ: GIL это Global Interpreter Lock
GIL (Global Interpreter Lock) — это мьютекс, который защищает объекты Python в памяти. Он позволяет только одному потоку одновременно выполнять Python-код в CPython интерпретаторе, даже на многоядерных процессорах.
Почему GIL существует
Python объекты требуют управления памятью через подсчёт ссылок (reference counting). Без GIL пришлось бы:
- Защищать каждый объект своим мьютексом (сложно)
- Риск deadlock-ов (высокий)
- Огромное замедление (каждый доступ — блокировка)
GIL решает это просто: один глобальный мьютекс для всего интерпретатора.
# Пример того, почему нужна синхронизация:
# Если два потока одновременно увеличат счётчик ссылок:
# Поток 1: obj.ref_count += 1
# Поток 2: obj.ref_count += 1
# Результат: increment только на 1, вместо 2 (race condition)
# GIL гарантирует, что это не случится
Как GIL ломает многопоточность
Проблема 1: Только один поток за раз
import threading
import time
def cpu_bound_work():
"""CPU-интенсивная работа"""
total = 0
for i in range(100_000_000):
total += i
return total
def single_thread():
"""Один поток — два вызова"""
start = time.time()
cpu_bound_work()
cpu_bound_work()
return time.time() - start
def multi_thread():
"""Два потока одновременно"""
start = time.time()
t1 = threading.Thread(target=cpu_bound_work)
t2 = threading.Thread(target=cpu_bound_work)
t1.start()
t2.start()
t1.join()
t2.join()
return time.time() - start
print(f"Single thread: {single_thread():.2f}s") # ~5.2s
print(f"Multi thread: {multi_thread():.2f}s") # ~5.5s (даже медленнее!)
# Два потока медленнее одного — из-за overhead GIL
Проблема 2: GIL выпускается во время I/O
import threading
import time
import requests
def fetch_url():
"""I/O операция (GIL выпускается)"""
# Здесь GIL отпускается — другой поток может работать
requests.get("https://api.example.com")
def single_io():
start = time.time()
fetch_url()
fetch_url()
return time.time() - start # ~4 секунды (два запроса подряд)
def multi_io():
start = time.time()
t1 = threading.Thread(target=fetch_url)
t2 = threading.Thread(target=fetch_url)
t1.start()
t2.start()
t1.join()
t2.join()
return time.time() - start # ~2 секунды (параллельно!)
print(f"Single: {single_io():.1f}s")
print(f"Multi: {multi_io():.1f}s")
Вывод: Threading работает для I/O, не для CPU.
Когда GIL отпускается
- Во время I/O операций:
socket,file,requests,database - В numpy/C расширениях: код на C обходит GIL
- В
time.sleep(): поток спит
В эти моменты другие потоки могут работать.
Решения
1. multiprocessing — для CPU-bound
from multiprocessing import Pool
def cpu_work(n):
total = 0
for i in range(n):
total += i
return total
if __name__ == "__main__":
# Каждый процесс имеет свой интерпретатор и GIL
with Pool(4) as p:
results = p.map(cpu_work, [100_000_000] * 4)
print(results)
2. asyncio — для I/O-bound
import asyncio
import aiohttp
async def fetch_many():
async with aiohttp.ClientSession() as session:
# Кооперативная многозадачность без потоков
tasks = [
session.get(f"https://api.example.com/{i}")
for i in range(100)
]
return await asyncio.gather(*tasks)
asyncio.run(fetch_many())
3. C расширения — NumPy, Pandas
import numpy as np
# NumPy операции выпускают GIL
arr = np.arange(100_000_000)
result = np.sum(arr) # Это работает параллельно на многоядрах!
4. Python 3.13+ — Free-threading (экспериментально)
В новых версиях Python начинают убирать GIL, но пока это не стабильно.
Таблица: Когда использовать что
| Тип работы | Решение | Описание |
|---|---|---|
| CPU-bound | multiprocessing | Отдельные процессы, каждый со своим GIL |
| I/O-bound (сеть) | asyncio | Кооперативная многозадачность |
| I/O-bound (файлы) | threading + concurrent.futures | GIL отпускается на файловых операциях |
| NumPy вычисления | Встроенные функции | C расширения выпускают GIL |
Итог
- GIL существует для безопасного управления памятью в CPython
- Он ломает threading для CPU-bound задач
- Для CPU: используй
multiprocessing - Для I/O: используй
asyncioилиthreading - Помни: GIL — это реальность CPython, с этим нужно работать, а не бороться