Короткий ответ
GIL (Global Interpreter Lock) — блокировка в эталонной реализации CPython, которая позволяет только одному потоку исполнять байткод Python в любой момент времени. Она появилась из-за непотокобезопасного подсчёта ссылок, ограничивает вычислительные задачи, а вот задачи ввода-вывода от потоков по-прежнему выигрывают.
Как это работает подробнее
Многопоточность в Python существует, но из-за GIL реально параллельно по байткоду исполняется только один поток.
- Суть механизма. GIL — мьютекс, удерживаемый единственным потоком: даже в многоядерной системе два потока одного процесса не выполняют Python-байткод одновременно.
- Причина появления. Управление памятью в CPython, в частности подсчёт ссылок, не потокобезопасно; GIL упрощает его и избавляет разработчиков от множества ошибок с общими объектами.
- Что страдает. CPU-bound программы не ускоряются от добавления потоков, тогда как I/O-bound задачи дают выигрыш благодаря ожиданиям ввода-вывода.
- Обходные пути. Настоящий параллелизм дают отдельные процессы через
multiprocessingи внешние C-библиотеки вроде NumPy, которые освобождают GIL при вычислениях.
Пример кода
Синхронизация потоков блокировкой
# GIL не позволяет двум потокам исполнять Python-байткод одновременно
import threading
counter = 0
lock = threading.Lock()
def worker():
global counter
for _ in range(100000):
with lock: # синхронизация общего состояния
counter += 1
threads = [threading.Thread(target=worker) for _ in range(2)]
for t in threads: t.start()
for t in threads: t.join()
print(counter)Что использовать на практике
- I/O-bound задачи. Для ожидания сети, диска и API используй потоки — они дают выигрыш.