TGViewer
Находки в опенсорсе Находки в опенсорсе @opensource_findings · 12.6K subscribers
Post #888 11.5K
Что такое GIL в Python? Вторая часть

Я не закончил! 🌚

Мы не поговорили про очень важную часть: переключение тредов интерпретатором. Треды могут быть очень долгими и не отпускать GIL, мы должны дать поработать каждому.

Простой пример, что так оно и работает:


import threading

def first():
while True:
print('first')

def two():
while True:
print('two')

a = threading.Thread(target=first).start()
b = threading.Thread(target=two).start()


Выдаст что-то вроде:


first
two
first
first
two
two
two
first
first


Как VM CPython переключает потоки?

Важно: у CPython нет своего scheduler'а для тредов. Он целиком полагается на OS в данном вопросе. Однако, GIL дает возможность переодически останавливать работу с Python API одного потока и дать время какому-то другому. Какому – решает уже OS. Но как?

Во-первых, у нас есть замечательный sys.setswitchinterval и sys.getswitchinterval, которые отвечают за примерное время работы одного потока, которое хранится в _gil_runtime_state:


unsigned long _PyEval_GetSwitchInterval(void)
{
PyInterpreterState *interp = _PyInterpreterState_GET();
struct _gil_runtime_state *gil = interp->ceval.gil;
assert(gil != NULL);
return gil->interval;
}


Примерный вывод:


>>> import sys
>>> sys.getswitchinterval()
0.005


Далее: значение gil->interval будет использовано для вызова pthread_cond_timedwait. Мы ждем interval для передачи события (чтобы нам дали GIL) через gil->cond. Если уходим в таймаут, значит пришло время забирать GIL на следующем выполнении байткода силой. Смотри про сигналы и PyCOND_T тут.


MUTEX_LOCK(gil->mutex);

unsigned long interval = (gil->interval >= 1 ? gil->interval : 1);
int timed_out = 0;
COND_TIMED_WAIT(gil->cond, gil->mutex, interval, timed_out);

/* If we timed out and no switch occurred in the meantime, it is time
to ask the GIL-holding thread to drop it. */
if (timed_out && _Py_atomic_load_int_relaxed(&gil->locked)) {
PyThreadState *holder_tstate =
(PyThreadState*)_Py_atomic_load_ptr_relaxed(&gil->last_holder);
assert(_PyThreadState_CheckConsistency(tstate));

_Py_set_eval_breaker_bit(holder_tstate, _PY_GIL_DROP_REQUEST_BIT);
}


Когда флаг _PY_GIL_DROP_REQUEST_BIT будет установлен, мы сможем в _Py_HandlePending передать GIL кому-то другому:


/* GIL drop request */
if ((breaker & _PY_GIL_DROP_REQUEST_BIT) != 0) {
/* Give another thread a chance */
_PyThreadState_Detach(tstate);

/* Other threads may run now */

_PyThreadState_Attach(tstate);
}


После вызова _PyThreadState_Detach(tstate) текущий тред потеряет GIL. И снова будет ждать его при вызове _PyThreadState_Attach(tstate). Пока другой работает.

Часть про VM

Теперь вопрос, а кто вызывает _Py_HandlePending и когда?

Вызывается оно из специального "псевдо-опкода" _CHECK_PERIODIC. Раньше там был макрос CHECK_EVAL_BREAKER, и его иногда забывали добавить в нужные места. Оттого события ОС не обрабатывались, GIL не переключался, было весело.


op(_CHECK_PERIODIC, (--)) {
_Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY();
QSBR_QUIESCENT_STATE(tstate);
if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) {
int err = _Py_HandlePending(tstate);
ERROR_IF(err != 0, error);
}
}

// Вызываем `_CHECK_PERIODIC` в конце каждого `CALL`.
macro(CALL) =
_SPECIALIZE_CALL
+ unused/2
+ _MAYBE_EXPAND_METHOD
+ _DO_CALL
+ _CHECK_PERIODIC;


Вот теперь мы знаем, как переключаются треды внутри CPython.

Одной строкой

- Замечательная (и более длинная) статья Андрея Светлова на русском про GIL, которую не вчера скинули в нашем чате (реклама)
- Предложение по отмене PyGILState АПИ, потому что оно не работает нормально с субинтерпретаторами

Если интересно – закидывайте в коллег!

| Поддержать | YouTube | GitHub | Чат |
Python documentation sys — System-specific parameters and functions This module provides access to some variables used or maintained by the interpreter and to functions that interact strongly with the interpreter. It is always available. Unless explicitly noted oth...
  • 👍 72
  • ❤ 23
  • 🔥 5
  • 👏 1
  • 🤯 1
  • 🤡 1
More from @opensource_findings
  1. Sep 25, 2026Монадические выражения в Python. Наконец-то! Продолжаем разговор про интересные PEPы. Пого…
  2. Sep 15, 2026PEPы в Python окончательно вышли из-под контроля Давайте посмотрим, что происходит с ПЕПам…
  3. Sep 10, 2026Большая бесплатная конференция в Нижнем Новгороде 17 октября Регистрация: https://itgorky.…
  4. Sep 1, 2026Post #985
  5. Aug 24, 2026JIT могут удалить из CPython! PEP-836: https://peps.python.org/pep-0836 JIT в CPython имее…
  6. Aug 18, 2026Как AWS у опенсорсера пакеты отжимал Нерегулярная рубрика "посмотрите, что творится!". Дан…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →