synchronization.md
1> **Источник:** https://python-all.ru/3.15/c-api/synchronization.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# Примитивы синхронизации89C-API предоставляет базовую блокировку взаимного исключения.1011#### `type PyMutex`1213Блокировка взаимного исключения. `PyMutex` следует инициализировать нулём для представления разблокированного состояния. Например:1415```c16PyMutex mutex = {0};17```1819Экземпляры `PyMutex` не должны копироваться или перемещаться. Как содержимое, так и адрес `PyMutex` имеют смысл, и он должен оставаться в фиксированном, доступном для записи месте в памяти.2021> **Примечание**22>23> `PyMutex` в настоящее время занимает один байт, но размер следует считать нестабильным. Размер может измениться в будущих выпусках Python без периода устаревания.2425Добавлено в версии 3.13.2627#### `void PyMutex_Lock(PyMutex *m)`2829*Потокобезопасность: [атомарно](https://python-all.ru/3.15/library/threadsafety.html#threadsafety-level-atomic).*3031Блокирует мьютекс *m*. Если другой поток уже заблокировал его, вызывающий поток будет блокироваться до разблокировки мьютекса. На время блокировки поток временно отсоединит [состояние потока](https://python-all.ru/3.15/glossary.html#term-attached-thread-state), если оно существует.3233Добавлено в версии 3.13.3435#### `void PyMutex_Unlock(PyMutex *m)`3637*Потокобезопасность: [атомарно](https://python-all.ru/3.15/library/threadsafety.html#threadsafety-level-atomic).*3839Разблокирует мьютекс *m*. Мьютекс должен быть заблокирован – в противном случае функция выдаст фатальную ошибку.4041Добавлено в версии 3.13.4243#### `int PyMutex_IsLocked(PyMutex *m)`4445*Потокобезопасность: [атомарно](https://python-all.ru/3.15/library/threadsafety.html#threadsafety-level-atomic).*4647Возвращает ненулевое значение, если мьютекс *m* в данный момент заблокирован, и ноль в противном случае.4849> **Примечание**50>51> Эта функция предназначена только для использования в утверждениях и отладке и не должна использоваться для принятия решений по управлению параллелизмом, так как состояние блокировки может измениться сразу после проверки.5253Добавлено в версии 3.14.5455## API критических секций Python5657API критических секций предоставляет уровень предотвращения взаимных блокировок поверх объектных блокировок для [свободно-поточного](https://python-all.ru/3.15/glossary.html#term-free-threading) CPython. Они предназначены для замены зависимости от [глобальной блокировки интерпретатора](https://python-all.ru/3.15/glossary.html#term-global-interpreter-lock) и являются пустышками (no-op) в версиях Python с глобальной блокировкой интерпретатора.5859Критические секции предназначены для использования с пользовательскими типами, реализованными в расширениях C-API. Их обычно не следует использовать со встроенными типами, такими как [`list`](https://python-all.ru/3.15/library/stdtypes.html#list) и [`dict`](https://python-all.ru/3.15/library/stdtypes.html#dict), поскольку их публичные C-API уже внутренне используют критические секции, за заметным исключением [`PyDict_Next()`](https://python-all.ru/3.15/c-api/dict.html#c.PyDict_Next), для которого требуется внешнее получение критической секции.6061Критические секции предотвращают взаимные блокировки, неявно приостанавливая активные критические секции, поэтому они не предоставляют исключительный доступ, как традиционные блокировки, например [`PyMutex`](https://python-all.ru/3.15/c-api/synchronization.html#c.PyMutex). При запуске критической секции захватывается объектная блокировка для данного объекта. Если код, выполняемый внутри критической секции, вызывает функции C-API, то он может приостановить критическую секцию, тем самым освободив объектную блокировку, так что другие потоки смогут захватить объектную блокировку для того же объекта.6263Также доступны варианты, принимающие указатели [`PyMutex`](https://python-all.ru/3.15/c-api/synchronization.html#c.PyMutex) вместо объектов Python. Используйте эти варианты для запуска критической секции в ситуации, когда нет [`PyObject`](https://python-all.ru/3.15/c-api/structures.html#c.PyObject) – например, при работе с типом C, который не расширяет и не оборачивает `PyObject`, но всё равно нуждается в вызове C-API способом, который может привести к взаимным блокировкам.6465> **Примечание**66>67> Операции, которым необходимо заблокировать два объекта одновременно, должны использовать [`Py_BEGIN_CRITICAL_SECTION2`](https://python-all.ru/3.15/c-api/synchronization.html#c.Py_BEGIN_CRITICAL_SECTION2). *Нельзя* использовать вложенные критические секции для блокировки более одного объекта одновременно, поскольку внутренняя критическая секция может приостановить внешние критические секции. Этот API не предоставляет способа заблокировать более двух объектов одновременно.6869Пример использования:7071```c72static PyObject *73set_field(MyObject *self, PyObject *value)74{75 Py_BEGIN_CRITICAL_SECTION(self);76 Py_SETREF(self->field, Py_XNewRef(value));77 Py_END_CRITICAL_SECTION();78 Py_RETURN_NONE;79}80```8182В приведённом выше примере [`Py_SETREF`](https://python-all.ru/3.15/c-api/refcounting.html#c.Py_SETREF) вызывает [`Py_DECREF`](https://python-all.ru/3.15/c-api/refcounting.html#c.Py_DECREF), который может вызывать произвольный код через функцию освобождения объекта. API критических секций избегает потенциальных взаимных блокировок из-за повторного входа и порядка блокировок, позволяя среде выполнения временно приостанавливать критическую секцию, если код, запущенный финализатором, блокируется и вызывает [`PyEval_SaveThread()`](https://python-all.ru/3.15/c-api/threads.html#c.PyEval_SaveThread).8384#### `Py_BEGIN_CRITICAL_SECTION(op)`8586*Часть [Stable ABI](https://python-all.ru/3.15/c-api/stable.html#stable) начиная с версии 3.15.*8788Захватывает объектную блокировку для объекта *op* и начинает критическую секцию.8990В сборке free-threaded и при сборке для [Stable ABI](https://python-all.ru/3.15/c-api/stable.html#stable-abi) этот макрос раскрывается в:9192```c93{94 PyCriticalSection _py_cs;95 PyCriticalSection_Begin(&_py_cs, (PyObject*)(op))96```9798В сборке по умолчанию этот макрос раскрывается в `{`.99100Добавлено в версии 3.13.101102#### `Py_BEGIN_CRITICAL_SECTION_MUTEX(m)`103104Блокирует мьютекс *m* и начинает критическую секцию.105106В сборке free-threaded этот макрос раскрывается в:107108```c109{110 PyCriticalSection _py_cs;111 PyCriticalSection_BeginMutex(&_py_cs, m)112```113114Обратите внимание, что в отличие от [`Py_BEGIN_CRITICAL_SECTION`](https://python-all.ru/3.15/c-api/synchronization.html#c.Py_BEGIN_CRITICAL_SECTION), для аргумента макроса нет приведения – это должен быть указатель на [`PyMutex`](https://python-all.ru/3.15/c-api/synchronization.html#c.PyMutex).115116В сборке по умолчанию этот макрос раскрывается в `{`.117118Добавлено в версии 3.14.119120#### `Py_END_CRITICAL_SECTION()`121122*Часть [Stable ABI](https://python-all.ru/3.15/c-api/stable.html#stable) начиная с версии 3.15.*123124Завершает критическую секцию и освобождает блокировку объекта.125126В сборке free-threaded и при сборке для [Stable ABI](https://python-all.ru/3.15/c-api/stable.html#stable-abi) этот макрос раскрывается в:127128```c129 PyCriticalSection_End(&_py_cs);130}131```132133В сборке по умолчанию этот макрос раскрывается в `}`.134135Добавлено в версии 3.13.136137#### `Py_BEGIN_CRITICAL_SECTION2(a, b)`138139*Часть [Stable ABI](https://python-all.ru/3.15/c-api/stable.html#stable) начиная с версии 3.15.*140141Захватывает блокировки объектов для объектов *a* и *b* и начинает критическую секцию. Блокировки захватываются в одном порядке (сначала наименьший адрес), чтобы избежать взаимоблокировок из-за порядка захвата.142143В сборке free-threaded этот макрос раскрывается в:144145```c146{147 PyCriticalSection2 _py_cs2;148 PyCriticalSection2_Begin(&_py_cs2, (PyObject*)(a), (PyObject*)(b))149```150151В сборке по умолчанию этот макрос раскрывается в `{`.152153Добавлено в версии 3.13.154155#### `Py_BEGIN_CRITICAL_SECTION2_MUTEX(m1, m2)`156157Блокирует мьютексы *m1* и *m2* и начинает критическую секцию.158159В сборке free-threaded и при сборке для [Stable ABI](https://python-all.ru/3.15/c-api/stable.html#stable-abi) этот макрос раскрывается в:160161```c162{163 PyCriticalSection2 _py_cs2;164 PyCriticalSection2_BeginMutex(&_py_cs2, m1, m2)165```166167Обратите внимание, что в отличие от [`Py_BEGIN_CRITICAL_SECTION2`](https://python-all.ru/3.15/c-api/synchronization.html#c.Py_BEGIN_CRITICAL_SECTION2), для аргументов макроса не требуется приведение типов – они должны быть указателями [`PyMutex`](https://python-all.ru/3.15/c-api/synchronization.html#c.PyMutex).168169В сборке по умолчанию этот макрос раскрывается в `{`.170171Добавлено в версии 3.14.172173#### `Py_END_CRITICAL_SECTION2()`174175*Часть [Stable ABI](https://python-all.ru/3.15/c-api/stable.html#stable) начиная с версии 3.15.*176177Завершает критическую секцию и освобождает блокировки объектов.178179В сборке free-threaded и при сборке для [Stable ABI](https://python-all.ru/3.15/c-api/stable.html#stable-abi) этот макрос раскрывается в:180181```c182 PyCriticalSection2_End(&_py_cs2);183}184```185186В сборке по умолчанию этот макрос раскрывается в `}`.187188Добавлено в версии 3.13.189190### Низкоуровневый API критических секций191192Следующие функции и структуры предоставляются для случаев, когда макросы C недоступны.193194#### `void PyCriticalSection_Begin(PyCriticalSection *c, PyObject *op)`195196#### `void PyCriticalSection_End(PyCriticalSection *c)`197198#### `void PyCriticalSection2_Begin(PyCriticalSection2 *c, PyObject *a, PyObject *b)`199200#### `void PyCriticalSection2_End(PyCriticalSection2 *c);`201202*Часть [Stable ABI](https://python-all.ru/3.15/c-api/stable.html#stable) начиная с версии 3.15.*203204Следует использовать только так, как показано в развёртках макросов, перечисленных [ранее в этом разделе](https://python-all.ru/3.15/c-api/synchronization.html#critical-section-macros).205206В сборках CPython, отличных от [free-threaded](https://python-all.ru/3.15/glossary.html#term-free-threading), эти функции ничего не делают.207208Добавлено в версии 3.13.209210#### `type PyCriticalSection`211212#### `type PyCriticalSection2`213214*Часть [Stable ABI](https://python-all.ru/3.15/c-api/stable.html#stable) (включая все члены) начиная с версии 3.15.*215216Следует использовать только так, как показано в развёртках макросов, перечисленных [ранее в этом разделе](https://python-all.ru/3.15/c-api/synchronization.html#critical-section-macros). Обратите внимание, что содержимое структур является закрытым (private) и его значение может измениться в будущих версиях Python.217218Добавлено в версии 3.13.219220#### `void PyCriticalSection_BeginMutex(PyCriticalSection *c, PyMutex *m);`221222#### `void PyCriticalSection2_BeginMutex(PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2);`223224Следует использовать только так, как показано в развёртках макросов, перечисленных [ранее в этом разделе](https://python-all.ru/3.15/c-api/synchronization.html#critical-section-macros).225226В сборках CPython, отличных от [free-threaded](https://python-all.ru/3.15/glossary.html#term-free-threading), эти функции ничего не делают.227228Добавлено в версии 3.14.229230## Устаревшие API блокировок231232Эти API устарели с Python 3.13 с введением [`PyMutex`](https://python-all.ru/3.15/c-api/synchronization.html#c.PyMutex).233234Изменено в версии 3.15: Теперь эти API представляют собой простую обёртку вокруг `PyMutex`.235236#### `type PyThread_type_lock`237238Указатель на блокировку взаимного исключения.239240#### `type PyLockStatus`241242Результат захвата блокировки с таймаутом.243244#### `enumerator PY_LOCK_FAILURE`245246Не удалось захватить блокировку.247248#### `enumerator PY_LOCK_ACQUIRED`249250Блокировка успешно захвачена.251252#### `enumerator PY_LOCK_INTR`253254Захват блокировки прерван сигналом.255256#### `PyThread_type_lock PyThread_allocate_lock(void)`257258*Часть [Stable ABI](https://python-all.ru/3.15/c-api/stable.html#stable).*259260Выделить новую блокировку.261262В случае успеха функция возвращает блокировку; в случае неудачи возвращает `0` без установленного исключения.263264Вызывающий код не должен удерживать [присоединённое состояние потока](https://python-all.ru/3.15/glossary.html#term-attached-thread-state).265266Изменено в версии 3.15: Теперь эта функция всегда использует [`PyMutex`](https://python-all.ru/3.15/c-api/synchronization.html#c.PyMutex). В предыдущих версиях использовалась блокировка, предоставляемая операционной системой.267268#### `void PyThread_free_lock(PyThread_type_lock lock)`269270*Часть [Stable ABI](https://python-all.ru/3.15/c-api/stable.html#stable).*271272Уничтожить *блокировку*. Блокировка не должна удерживаться ни одним потоком при вызове этой функции.273274Вызывающий код не должен удерживать [присоединённое состояние потока](https://python-all.ru/3.15/glossary.html#term-attached-thread-state).275276#### `PyLockStatus PyThread_acquire_lock_timed(PyThread_type_lock lock, long long microseconds, int intr_flag)`277278*Часть [Stable ABI](https://python-all.ru/3.15/c-api/stable.html#stable).*279280Захватить *блокировку* с таймаутом.281282Будет ожидать *microseconds* микросекунд для захвата блокировки. Если таймаут истекает, функция возвращает [`PY_LOCK_FAILURE`](https://python-all.ru/3.15/c-api/synchronization.html#c.PY_LOCK_FAILURE). Если *microseconds* равно `-1`, будет ожидать неопределённо долго, пока блокировка не будет освобождена.283284Если *intr\_flag* равно `1`, захват блокировки может быть прерван сигналом, и в этом случае функция возвращает [`PY_LOCK_INTR`](https://python-all.ru/3.15/c-api/synchronization.html#c.PY_LOCK_INTR). После прерывания ожидается, что вызывающая сторона вызовет [`Py_MakePendingCalls()`](https://python-all.ru/3.15/c-api/threads.html#c.Py_MakePendingCalls) для передачи исключения в код Python.285286Если блокировка успешно захвачена, функция возвращает [`PY_LOCK_ACQUIRED`](https://python-all.ru/3.15/c-api/synchronization.html#c.PY_LOCK_ACQUIRED).287288Вызывающий код не должен удерживать [присоединённое состояние потока](https://python-all.ru/3.15/glossary.html#term-attached-thread-state).289290#### `int PyThread_acquire_lock(PyThread_type_lock lock, int waitflag)`291292*Часть [Stable ABI](https://python-all.ru/3.15/c-api/stable.html#stable).*293294Захватить *блокировку*.295296Если *waitflag* равно `1`, и другой поток в данный момент удерживает блокировку, эта функция будет ждать, пока блокировка не будет захвачена, и всегда будет возвращать `1`.297298Если *waitflag* равно `0`, и другой поток удерживает блокировку, эта функция не будет ждать, а вернёт `0`. Если блокировка не удерживается никаким другим потоком, то эта функция захватит её и вернёт `1`.299300В отличие от [`PyThread_acquire_lock_timed()`](https://python-all.ru/3.15/c-api/synchronization.html#c.PyThread_acquire_lock_timed), захват блокировки не может быть прерван сигналом.301302Вызывающий код не должен удерживать [присоединённое состояние потока](https://python-all.ru/3.15/glossary.html#term-attached-thread-state).303304#### `int PyThread_release_lock(PyThread_type_lock lock)`305306*Часть [Stable ABI](https://python-all.ru/3.15/c-api/stable.html#stable).*307308Освободить *блокировку*. Если *блокировка* не удерживается, то эта функция вызовет фатальную ошибку.309310Вызывающий код не должен удерживать [присоединённое состояние потока](https://python-all.ru/3.15/glossary.html#term-attached-thread-state).311