Ответ
Планировщик потоков в Python (CPython) — это стандартный планировщик операционной системы, но его работа тесно связана с Global Interpreter Lock (GIL).
Что такое GIL?
GIL (Global Interpreter Lock) — это мьютекс (глобальная блокировка), который защищает доступ к объектам Python, позволяя только одному потоку выполнять байт-код Python в любой момент времени в рамках одного процесса. Он не является планировщиком, а лишь ограничивает его.
Почему он существует?
GIL упрощает управление памятью в CPython (автоматический подсчет ссылок становится потокобезопасным) и облегчает интеграцию с C-библиотеками, которые не являются потокобезопасными.
Механизм переключения потоков:
Поток, владеющий GIL, освобождает его, и планировщик передает управление другому потоку в следующих случаях:
- Операции ввода-вывода (I/O): При ожидании ответа от сети, диска или базы данных поток освобождает GIL, позволяя другим потокам выполняться.
- Явное освобождение: Вызов
time.sleep(). - По истечении интервала: Поток принудительно освобождает GIL после выполнения определенного количества инструкций байт-кода (интервал можно получить через
sys.getswitchinterval()). Это предотвращает «зависание» CPU-bound потоков.
import threading
import time
# Эта функция будет выполняться в потоке
def io_bound_task():
print(f"{threading.current_thread().name} начал I/O операцию...")
# Во время sleep() поток освобождает GIL, давая работать другим
time.sleep(1)
print(f"{threading.current_thread().name} завершил I/O операцию.")
# Создаем и запускаем два потока
thread1 = threading.Thread(target=io_bound_task, name="Поток 1")
thread2 = threading.Thread(target=io_bound_task, name="Поток 2")
thread1.start()
thread2.start()
thread1.join()
thread2.join()
# Вывод покажет, что потоки выполняются псевдо-параллельно
Вывод:
- Для I/O-bound задач (ожидание сети, диска)
threadingэффективен, так как GIL освобождается во время ожидания. - Для CPU-bound задач (математические вычисления)
threadingне дает прироста производительности. Для настоящего параллелизма на многоядерных системах следует использовать модульmultiprocessing, где каждый процесс получает собственный интерпретатор и собственный GIL.