← Назад к вопросам

Что такое GIL в Python?

2.0 Middle🔥 191 комментариев
#Python

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое GIL в Python?

GIL (Global Interpreter Lock) — это взаимное исключение (mutex), которое защищает доступ к объектам Python в интерпретаторе CPython. Это одна из самых важных особенностей Python, которая существенно влияет на многопоточное программирование.

Суть проблемы

Практически все объекты в Python имеют счётчик ссылок (reference count) для управления памятью через подсчёт ссылок. Если несколько потоков одновременно создавали или удаляли ссылки на один объект, счётчик мог бы повредиться, что привело бы к утечкам памяти или преждевременному удалению живых объектов.

# Пример проблемы без GIL
class MyObject:
    def __init__(self):
        self.ref_count = 0

# Если два потока одновременно выполнят это:
# obj.ref_count += 1
# Может произойти race condition и счётчик будет неправильным

Вместо того чтобы добавлять mutex к каждому объекту (что было бы медленно и опасно для deadlock), CPython использует один глобальный mutex — GIL.

Как работает GIL

В каждый момент времени только один поток может выполнять Python bytecode. Другие потоки вынуждены ждать освобождения GIL:

import threading
import time

def cpu_intensive_task():
    # Это будет медленнее с несколькими потоками!
    total = 0
    for i in range(100_000_000):
        total += i
    return total

# Однопоточное выполнение
start = time.time()
for _ in range(2):
    cpu_intensive_task()
print(f"Однопоток: {time.time() - start:.2f}s")

# Многопоточное выполнение
start = time.time()
threads = [
    threading.Thread(target=cpu_intensive_task)
    for _ in range(2)
]
for t in threads:
    t.start()
for t in threads:
    t.join()
print(f"Двух потоков: {time.time() - start:.2f}s")  # Примерно столько же!

Результат: многопоточное исполнение будет примерно такого же или даже медленнее из-за overhead переключения контекста.

Когда GIL НЕ проблема

1. I/O-bound операции (сетевые запросы, файловая система)

Когда поток ждёт ввода-вывода, он отпускает GIL:

import threading
import requests
import time

def fetch_url(url):
    response = requests.get(url)
    return len(response.text)

urls = [
    https://example.com,
    https://google.com,
    # ... 10 URLs
] * 3

# Многопоточное выполнение ДА ускоряет I/O операции
start = time.time()
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. Использование библиотек с собственным параллелизмом

NumPy, SciPy, Pandas часто отпускают GIL при выполнении вычислений:

import numpy as np
import threading
import time

def matrix_multiply():
    a = np.random.rand(1000, 1000)
    b = np.random.rand(1000, 1000)
    return np.dot(a, b)  # GIL отпущен!

start = time.time()
threads = [threading.Thread(target=matrix_multiply) for _ in range(4)]
for t in threads:
    t.start()
for t in threads:
    t.join()
print(f"NumPy с потоками: {time.time() - start:.2f}s")  # Хорошее масштабирование

Решения для CPU-bound задач

1. multiprocessing — несколько процессов, отдельный GIL для каждого

from multiprocessing import Pool
import time

def cpu_task(n):
    total = 0
    for i in range(n):
        total += i
    return total

# Используем несколько процессов
with Pool(4) as pool:
    start = time.time()
    results = pool.map(cpu_task, [100_000_000] * 4)
    print(f"Multiprocessing: {time.time() - start:.2f}s")  # ~4x ускорение

2. asyncio — асинхронный код, один поток

import asyncio

async def io_task():
    # Ждём I/O
    await asyncio.sleep(1)

async def main():
    # Все задачи запускаются параллельно в одном потоке
    start = time.time()
    await asyncio.gather(
        io_task(),
        io_task(),
        io_task(),
        io_task(),
    )
    print(f"Asyncio: {time.time() - start:.2f}s")  # ~1s вместо 4s

asyncio.run(main())

3. Cython/C extensions — скомпилированный код, отпускающий GIL

Python 3.13+: Новая надежда

Sam Gross предложил удалить GIL в Python 3.13 (экспериментальная опция). Это потенциально может изменить ландшафт многопоточного программирования в Python.

Итоги

  • GIL — это не багу, это feature для безопасности памяти
  • Для I/O-bound задач: threading работает отлично
  • Для CPU-bound задач: используй multiprocessing или asyncio
  • Для ML/Data Science: NumPy, Pandas, PyTorch часто отпускают GIL
  • Выбирай инструмент в зависимости от типа задачи