Ответ
GIL (Global Interpreter Lock) — это мьютекс (mutex) в стандартном интерпретаторе CPython, который разрешает исполнение Python-байткода только одному потоку (thread) в один момент времени.
Основное назначение:
- Защита управления памятью. GIL упрощает управление памятью в CPython, делая операции со счётчиками ссылок (reference counting) потокобезопасными без необходимости сложных блокировок на каждом объекте.
- Упрощение интеграции с C-библиотеками. Обеспечивает безопасность при работе с C-библиотеками, которые не являются потокобезопасными.
Ключевое следствие: Из-за GIL многопоточные программы на Python, которые интенсивно используют CPU (CPU-bound), не могут достичь истинного параллелизма на многоядерных процессорах. Потоки выполняются поочередно на одном ядре.
Пример демонстрации проблемы: Два потока, выполняющие CPU-bound задачу, работают не быстрее (а часто медленнее из-за накладных расходов на переключение контекста), чем один поток.
import threading
import time
def cpu_bound_task():
count = 0
for i in range(10**7):
count += i
# Последовательное выполнение
start_time = time.time()
cpu_bound_task()
cpu_bound_task()
print(f"Sequential: {time.time() - start_time:.2f}s")
# Параллельное выполнение (в кавычках)
start_time = time.time()
thread1 = threading.Thread(target=cpu_bound_task)
thread2 = threading.Thread(target=cpu_bound_task)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print(f"With threads: {time.time() - start_time:.2f}s")
Как обойти GIL?
Для задач, требующих параллельных вычислений, используют модуль multiprocessing, который создает отдельные процессы, каждый со своим собственным интерпретатором Python и своим GIL. Для I/O-bound задач эффективно используется asyncio.