Вопрос проверяет, понимает ли кандидат, что GIL не решает проблему синхронизации данных между потоками.
GIL не защищает ваши данные — он защищает только внутренние структуры CPython. Потоки по-прежнему могут читать и изменять общие объекты почти одновременно, вызывая race conditions. Поэтому для корректного доступа к shared state используют Lock, RLock, Semaphore, Event, очереди сообщений и другие инструменты. Эти примитивы обеспечивают безопасную последовательность выполнения и гарантируют консистентность данных.
Многие считают, что наличие GIL делает многопоточность безопасной, но это заблуждение.
Определение:
GIL — глобальная блокировка интерпретатора CPython, разрешающая выполнение Python-байткода только одному потоку в конкретный момент времени.
GIL:
защищает внутренние структуры интерпретатора;
упрощает reference counting;
не гарантирует атомарность сложных операций Python-кода.
GIL не:
предотвращает логические гонки данных;
обеспечивает корректность модификации объектов на уровне бизнес-логики;
делает ваш код потокобезопасным.
Даже если поток выполняется «один за раз», переключение контекста может произойти в любой момент между операциями.
Например, операция counter += 1 не атомарна:
чтение значения
вычисление нового значения
запись результата
Если два потока выполнят эти шаги «вперемешку», данные будут потеряны.
Python
import threading
counter = 0
def wrong():
global counter
for _ in 100_000:
counter += 1 # неатомарно!
Этот код даст неправильный результат даже с GIL.
Lock / RLock
гарантируют, что только один поток выполняет критическую секцию кода.
Python
lock = threading.Lock()
def safe():
global counter
with lock:
counter += 1
Semaphore
ограничивает количество потоков, которым разрешён доступ к ресурсу.
Event, Condition
позволяют потокам ждать определённого состояния.
Очереди (queue.Queue)
исключают общий доступ к mutable state.
при работе со структурами данных (списки, словари, множества);
при модификации общих объектов;
при наличии фоновых потоков, изменяющих состояние приложения;
при интеграции с I/O-операциями, где переключение потоков происходит часто.
GIL не обеспечивает потокобезопасности пользовательского кода. Примитивы синхронизации нужны, чтобы гарантировать атомарность операций, избежать гонок данных и обеспечить корректную работу многопоточного приложения.