Документация Python неофициальный перевод
Содержание страницы

Примитивы синхронизацииSynchronization primitives

C-API предоставляет базовую блокировку взаимного исключения.

type PyMutex

Блокировка взаимного исключения. PyMutex следует инициализировать нулём для представления разблокированного состояния. Например:

PyMutex mutex = {0};

Экземпляры PyMutex не должны копироваться или перемещаться. Как содержимое, так и адрес PyMutex имеют смысл, и он должен оставаться в фиксированном, доступном для записи месте в памяти.

Примечание

PyMutex в настоящее время занимает один байт, но размер следует считать нестабильным. Размер может измениться в будущих выпусках Python без периода устаревания.

Добавлено в версии 3.13.

void PyMutex_Lock(PyMutex *m)
Потокобезопасность: атомарно.

Блокирует мьютекс m. Если другой поток уже заблокировал его, вызывающий поток будет блокироваться до разблокировки мьютекса. На время блокировки поток временно отсоединит состояние потока, если оно существует.

Добавлено в версии 3.13.

void PyMutex_Unlock(PyMutex *m)
Потокобезопасность: атомарно.

Разблокирует мьютекс m. Мьютекс должен быть заблокирован – в противном случае функция выдаст фатальную ошибку.

Добавлено в версии 3.13.

int PyMutex_IsLocked(PyMutex *m)
Потокобезопасность: атомарно.

Возвращает ненулевое значение, если мьютекс m в данный момент заблокирован, и ноль в противном случае.

Примечание

Эта функция предназначена только для использования в утверждениях и отладке и не должна использоваться для принятия решений по управлению параллелизмом, так как состояние блокировки может измениться сразу после проверки.

Добавлено в версии 3.14.

API критических секций PythonPython critical section API

API критических секций предоставляет уровень предотвращения взаимных блокировок поверх объектных блокировок для свободно-поточного CPython. Они предназначены для замены зависимости от глобальной блокировки интерпретатора и являются пустышками (no-op) в версиях Python с глобальной блокировкой интерпретатора.

Критические секции предназначены для использования с пользовательскими типами, реализованными в расширениях C-API. Их обычно не следует использовать со встроенными типами, такими как list и dict, поскольку их публичные C-API уже внутренне используют критические секции, за заметным исключением PyDict_Next(), для которого требуется внешнее получение критической секции.

Критические секции предотвращают взаимные блокировки, неявно приостанавливая активные критические секции, поэтому они не предоставляют исключительный доступ, как традиционные блокировки, например PyMutex. При запуске критической секции захватывается объектная блокировка для данного объекта. Если код, выполняемый внутри критической секции, вызывает функции C-API, то он может приостановить критическую секцию, тем самым освободив объектную блокировку, так что другие потоки смогут захватить объектную блокировку для того же объекта.

Также доступны варианты, принимающие указатели PyMutex вместо объектов Python. Используйте эти варианты для запуска критической секции в ситуации, когда нет PyObject – например, при работе с типом C, который не расширяет и не оборачивает PyObject, но всё равно нуждается в вызове C-API способом, который может привести к взаимным блокировкам.

Примечание

Операции, которым необходимо заблокировать два объекта одновременно, должны использовать Py_BEGIN_CRITICAL_SECTION2. Нельзя использовать вложенные критические секции для блокировки более одного объекта одновременно, поскольку внутренняя критическая секция может приостановить внешние критические секции. Этот API не предоставляет способа заблокировать более двух объектов одновременно.

Пример использования:

static PyObject *
set_field(MyObject *self, PyObject *value)
{
   Py_BEGIN_CRITICAL_SECTION(self);
   Py_SETREF(self->field, Py_XNewRef(value));
   Py_END_CRITICAL_SECTION();
   Py_RETURN_NONE;
}

В приведённом выше примере Py_SETREF вызывает Py_DECREF, который может вызывать произвольный код через функцию освобождения объекта. API критических секций избегает потенциальных взаимных блокировок из-за повторного входа и порядка блокировок, позволяя среде выполнения временно приостанавливать критическую секцию, если код, запущенный финализатором, блокируется и вызывает PyEval_SaveThread().

Py_BEGIN_CRITICAL_SECTION(op)
Часть Stable ABI начиная с версии 3.15.

Захватывает объектную блокировку для объекта op и начинает критическую секцию.

В сборке free-threaded и при сборке для Stable ABI этот макрос раскрывается в:

{
    PyCriticalSection _py_cs;
    PyCriticalSection_Begin(&_py_cs, (PyObject*)(op))

В сборке по умолчанию этот макрос раскрывается в {.

Добавлено в версии 3.13.

Py_BEGIN_CRITICAL_SECTION_MUTEX(m)

Блокирует мьютекс m и начинает критическую секцию.

В сборке free-threaded этот макрос раскрывается в:

{
     PyCriticalSection _py_cs;
     PyCriticalSection_BeginMutex(&_py_cs, m)

Обратите внимание, что в отличие от Py_BEGIN_CRITICAL_SECTION, для аргумента макроса нет приведения – это должен быть указатель на PyMutex.

В сборке по умолчанию этот макрос раскрывается в {.

Добавлено в версии 3.14.

Py_END_CRITICAL_SECTION()
Часть Stable ABI начиная с версии 3.15.

Завершает критическую секцию и освобождает блокировку объекта.

В сборке free-threaded и при сборке для Stable ABI этот макрос раскрывается в:

    PyCriticalSection_End(&_py_cs);
}

В сборке по умолчанию этот макрос раскрывается в }.

Добавлено в версии 3.13.

Py_BEGIN_CRITICAL_SECTION2(a, b)
Часть Stable ABI начиная с версии 3.15.

Захватывает блокировки объектов для объектов a и b и начинает критическую секцию. Блокировки захватываются в одном порядке (сначала наименьший адрес), чтобы избежать взаимоблокировок из-за порядка захвата.

В сборке free-threaded этот макрос раскрывается в:

{
    PyCriticalSection2 _py_cs2;
    PyCriticalSection2_Begin(&_py_cs2, (PyObject*)(a), (PyObject*)(b))

В сборке по умолчанию этот макрос раскрывается в {.

Добавлено в версии 3.13.

Py_BEGIN_CRITICAL_SECTION2_MUTEX(m1, m2)

Блокирует мьютексы m1 и m2 и начинает критическую секцию.

В сборке free-threaded и при сборке для Stable ABI этот макрос раскрывается в:

{
     PyCriticalSection2 _py_cs2;
     PyCriticalSection2_BeginMutex(&_py_cs2, m1, m2)

Обратите внимание, что в отличие от Py_BEGIN_CRITICAL_SECTION2, для аргументов макроса не требуется приведение типов – они должны быть указателями PyMutex.

В сборке по умолчанию этот макрос раскрывается в {.

Добавлено в версии 3.14.

Py_END_CRITICAL_SECTION2()
Часть Stable ABI начиная с версии 3.15.

Завершает критическую секцию и освобождает блокировки объектов.

В сборке free-threaded и при сборке для Stable ABI этот макрос раскрывается в:

    PyCriticalSection2_End(&_py_cs2);
}

В сборке по умолчанию этот макрос раскрывается в }.

Добавлено в версии 3.13.

Низкоуровневый API критических секцийLow-level critical section API

Следующие функции и структуры предоставляются для случаев, когда макросы C недоступны.

void PyCriticalSection_Begin(PyCriticalSection *c, PyObject *op)
void PyCriticalSection_End(PyCriticalSection *c)
void PyCriticalSection2_Begin(PyCriticalSection2 *c, PyObject *a, PyObject *b)
void PyCriticalSection2_End(PyCriticalSection2 *c);
Часть Stable ABI начиная с версии 3.15.

Следует использовать только так, как показано в развёртках макросов, перечисленных ранее в этом разделе.

В сборках CPython, отличных от free-threaded, эти функции ничего не делают.

Добавлено в версии 3.13.

type PyCriticalSection
type PyCriticalSection2
Часть Stable ABI (включая все члены) начиная с версии 3.15.

Следует использовать только так, как показано в развёртках макросов, перечисленных ранее в этом разделе. Обратите внимание, что содержимое структур является закрытым (private) и его значение может измениться в будущих версиях Python.

Добавлено в версии 3.13.

void PyCriticalSection_BeginMutex(PyCriticalSection *c, PyMutex *m);
void PyCriticalSection2_BeginMutex(PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2);

Следует использовать только так, как показано в развёртках макросов, перечисленных ранее в этом разделе.

В сборках CPython, отличных от free-threaded, эти функции ничего не делают.

Добавлено в версии 3.14.

Устаревшие API блокировокLegacy locking APIs

Эти API устарели с Python 3.13 с введением PyMutex.

Изменено в версии 3.15: Теперь эти API представляют собой простую обёртку вокруг PyMutex.

type PyThread_type_lock

Указатель на блокировку взаимного исключения.

type PyLockStatus

Результат захвата блокировки с таймаутом.

enumerator PY_LOCK_FAILURE

Не удалось захватить блокировку.

enumerator PY_LOCK_ACQUIRED

Блокировка успешно захвачена.

enumerator PY_LOCK_INTR

Захват блокировки прерван сигналом.

PyThread_type_lock PyThread_allocate_lock(void)
Часть Stable ABI.

Выделить новую блокировку.

В случае успеха функция возвращает блокировку; в случае неудачи возвращает 0 без установленного исключения.

Вызывающий код не должен удерживать присоединённое состояние потока.

Изменено в версии 3.15: Теперь эта функция всегда использует PyMutex. В предыдущих версиях использовалась блокировка, предоставляемая операционной системой.

void PyThread_free_lock(PyThread_type_lock lock)
Часть Stable ABI.

Уничтожить блокировку. Блокировка не должна удерживаться ни одним потоком при вызове этой функции.

Вызывающий код не должен удерживать присоединённое состояние потока.

PyLockStatus PyThread_acquire_lock_timed(PyThread_type_lock lock, long long microseconds, int intr_flag)
Часть Stable ABI.

Захватить блокировку с таймаутом.

Будет ожидать microseconds микросекунд для захвата блокировки. Если таймаут истекает, функция возвращает PY_LOCK_FAILURE. Если microseconds равно -1, будет ожидать неопределённо долго, пока блокировка не будет освобождена.

Если intr_flag равно 1, захват блокировки может быть прерван сигналом, и в этом случае функция возвращает PY_LOCK_INTR. После прерывания ожидается, что вызывающая сторона вызовет Py_MakePendingCalls() для передачи исключения в код Python.

Если блокировка успешно захвачена, функция возвращает PY_LOCK_ACQUIRED.

Вызывающий код не должен удерживать присоединённое состояние потока.

int PyThread_acquire_lock(PyThread_type_lock lock, int waitflag)
Часть Stable ABI.

Захватить блокировку.

Если waitflag равно 1, и другой поток в данный момент удерживает блокировку, эта функция будет ждать, пока блокировка не будет захвачена, и всегда будет возвращать 1.

Если waitflag равно 0, и другой поток удерживает блокировку, эта функция не будет ждать, а вернёт 0. Если блокировка не удерживается никаким другим потоком, то эта функция захватит её и вернёт 1.

В отличие от PyThread_acquire_lock_timed(), захват блокировки не может быть прерван сигналом.

Вызывающий код не должен удерживать присоединённое состояние потока.

int PyThread_release_lock(PyThread_type_lock lock)
Часть Stable ABI.

Освободить блокировку. Если блокировка не удерживается, то эта функция вызовет фатальную ошибку.

Вызывающий код не должен удерживать присоединённое состояние потока.