Короткий ответ
GIL — глобальный мьютекс в CPython, который позволяет только одному потоку выполнять байт-код Python в любой конкретный момент времени, даже если в процессе запущено несколько потоков.
Как это работает подробнее
GIL — особенность конкретной реализации CPython (а не языка Python), введённая для упрощения управления памятью (счётчики ссылок не потокобезопасны сами по себе). Потоки по очереди захватывают GIL, выполняя небольшие порции байт-кода, и периодически его отдают.
Пример кода
example-250.py
import threading, time
def cpu_bound():
s = 0
for i in range(10_000_000):
s += i
start = time.perf_counter()
threads = [threading.Thread(target=cpu_bound) for _ in range(4)]
[t.start() for t in threads]
[t.join() for t in threads]
print(time.perf_counter() - start)
# Время не будет в ~4 раза меньше, чем для одного потока - GIL не даёт параллелизма CPU-bound кодуЧто использовать на практике
- Практика. Для CPU-bound задач (чистые вычисления на Python) многопоточность в CPython не даёт параллельного ускорения. Для I/O-bound задач GIL освобождается на время блокирующей операции, поэтому потоки эффективно помогают перекрывать ожидания.
Подводные камни
- Риски. Считают, что GIL автоматически делает любой код потокобезопасным — составная операция (
x += 1) может быть прервана между чтением и записью; путают GIL с отсутствием параллелизма вообще, забывая проmultiprocessingи C-расширения, освобождающие GIL.