free-threading-extensions.md
1> **Источник:** https://python-all.ru/3.13/howto/free-threading-extensions.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# Поддержка расширений C API для свободной многопоточности89Начиная с версии 3.13, CPython экспериментально поддерживает работу с отключённой [глобальной блокировкой интерпретатора](https://python-all.ru/3.13/glossary.html#term-global-interpreter-lock) (GIL) в конфигурации, называемой [свободной многопоточностью](https://python-all.ru/3.13/glossary.html#term-free-threading). В этом документе описано, как адаптировать расширения C API для поддержки свободной многопоточности.1011## Определение сборки со свободной многопоточностью в C1213C API CPython предоставляет макрос `Py_GIL_DISABLED`: в сборке со свободной многопоточностью он определён как `1`, а в обычной сборке не определён. Его можно использовать для включения кода, который выполняется только в сборке со свободной многопоточностью:1415```c16#ifdef Py_GIL_DISABLED17/* код, который выполняется только в сборке со свободной многопоточностью */18#endif19```2021> **Примечание**22>23> В Windows этот макрос не определяется автоматически, его нужно указать компилятору при сборке. Функция [`sysconfig.get_config_var()`](https://python-all.ru/3.13/library/sysconfig.html#sysconfig.get_config_var) может использоваться для проверки, был ли макрос определён в текущем запущенном интерпретаторе.2425## Инициализация модуля2627Модули расширения должны явно указать, что они поддерживают работу с отключённым GIL; иначе при импорте расширения будет выдано предупреждение и GIL будет включён во время выполнения.2829Есть два способа указать, что модуль расширения поддерживает работу с отключённым GIL, в зависимости от того, использует ли расширение многофазную или однофазную инициализацию.3031### Многофазная инициализация3233Расширения, использующие многофазную инициализацию (т.е. [`PyModuleDef_Init()`](https://python-all.ru/3.13/c-api/module.html#c.PyModuleDef_Init)), должны добавить слот [`Py_mod_gil`](https://python-all.ru/3.13/c-api/module.html#c.Py_mod_gil) в определение модуля. Если расширение поддерживает старые версии CPython, следует защитить слот проверкой [`PY_VERSION_HEX`](https://python-all.ru/3.13/c-api/apiabiversion.html#c.PY_VERSION_HEX).3435```c36static struct PyModuleDef_Slot module_slots[] = {37 ...38#if PY_VERSION_HEX >= 0x030D000039 {Py_mod_gil, Py_MOD_GIL_NOT_USED},40#endif41 {0, NULL}42};4344static struct PyModuleDef moduledef = {45 PyModuleDef_HEAD_INIT,46 .m_slots = module_slots,47 ...48};49```5051### Однофазная инициализация5253Расширения, использующие однофазную инициализацию (т.е. [`PyModule_Create()`](https://python-all.ru/3.13/c-api/module.html#c.PyModule_Create)), должны вызывать [`PyUnstable_Module_SetGIL()`](https://python-all.ru/3.13/c-api/module.html#c.PyUnstable_Module_SetGIL), чтобы указать, что они поддерживают работу с отключённым GIL. Эта функция определена только в free-threaded сборке, поэтому вызов следует защитить проверкой `#ifdef Py_GIL_DISABLED`, чтобы избежать ошибок компиляции в обычной сборке.5455```c56static struct PyModuleDef moduledef = {57 PyModuleDef_HEAD_INIT,58 ...59};6061PyMODINIT_FUNC62PyInit_mymodule(void)63{64 PyObject *m = PyModule_Create(&moduledef);65 if (m == NULL) {66 return NULL;67 }68#ifdef Py_GIL_DISABLED69 PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED);70#endif71 return m;72}73```7475## Общие рекомендации по API7677Большая часть C API потокобезопасна, но есть некоторые исключения.7879- **Поля структур**: прямой доступ к полям объектов C API Python или структур не является потокобезопасным, если поле может быть одновременно изменено.80- **Макросы**: Макросы-аксессоры, такие как [`PyList_GET_ITEM`](https://python-all.ru/3.13/c-api/list.html#c.PyList_GET_ITEM) и [`PyList_SET_ITEM`](https://python-all.ru/3.13/c-api/list.html#c.PyList_SET_ITEM), не выполняют никаких проверок ошибок и блокировок. Эти макросы не являются потокобезопасными, если объект-контейнер может изменяться одновременно.81- **Заимствованные ссылки**: функции C API, возвращающие [заимствованные ссылки](https://python-all.ru/3.13/glossary.html#term-borrowed-reference), могут не быть потокобезопасными, если содержащий объект изменяется одновременно. См. раздел [заимствованные ссылки](https://python-all.ru/3.13/howto/free-threading-extensions.html#id2) для получения дополнительной информации.8283### Потокобезопасность контейнеров8485Контейнеры, такие как [`PyListObject`](https://python-all.ru/3.13/c-api/list.html#c.PyListObject), [`PyDictObject`](https://python-all.ru/3.13/c-api/dict.html#c.PyDictObject) и [`PySetObject`](https://python-all.ru/3.13/c-api/set.html#c.PySetObject), выполняют внутреннюю блокировку в сборке со свободными потоками. Например, [`PyList_Append()`](https://python-all.ru/3.13/c-api/list.html#c.PyList_Append) будет блокировать список перед добавлением элемента.8687#### `PyDict_Next`8889Заметным исключением является [`PyDict_Next()`](https://python-all.ru/3.13/c-api/dict.html#c.PyDict_Next), который не блокирует словарь. Следует использовать [`Py_BEGIN_CRITICAL_SECTION`](https://python-all.ru/3.13/c-api/init.html#c.Py_BEGIN_CRITICAL_SECTION) для защиты словаря при итерации по нему, если словарь может быть одновременно изменён:9091```c92Py_BEGIN_CRITICAL_SECTION(dict);93PyObject *key, *value;94Py_ssize_t pos = 0;95while (PyDict_Next(dict, &pos, &key, &value)) {96 ...97}98Py_END_CRITICAL_SECTION();99```100101## Заимствованные ссылки102103Некоторые функции C API возвращают [заимствованные ссылки](https://python-all.ru/3.13/glossary.html#term-borrowed-reference). Эти API не являются потокобезопасными, если содержащий объект изменяется одновременно. Например, небезопасно использовать [`PyList_GetItem()`](https://python-all.ru/3.13/c-api/list.html#c.PyList_GetItem), если список может быть одновременно изменён.104105В следующей таблице перечислены некоторые API заимствованных ссылок и их замены, которые возвращают [сильные ссылки](https://python-all.ru/3.13/glossary.html#term-strong-reference).106107| API заимствованных ссылок | API сильных ссылок |108| --- | --- |109| [`PyList_GetItem()`](https://python-all.ru/3.13/c-api/list.html#c.PyList_GetItem) | [`PyList_GetItemRef()`](https://python-all.ru/3.13/c-api/list.html#c.PyList_GetItemRef) |110| [`PyList_GET_ITEM()`](https://python-all.ru/3.13/c-api/list.html#c.PyList_GET_ITEM) | [`PyList_GetItemRef()`](https://python-all.ru/3.13/c-api/list.html#c.PyList_GetItemRef) |111| [`PyDict_GetItem()`](https://python-all.ru/3.13/c-api/dict.html#c.PyDict_GetItem) | [`PyDict_GetItemRef()`](https://python-all.ru/3.13/c-api/dict.html#c.PyDict_GetItemRef) |112| [`PyDict_GetItemWithError()`](https://python-all.ru/3.13/c-api/dict.html#c.PyDict_GetItemWithError) | [`PyDict_GetItemRef()`](https://python-all.ru/3.13/c-api/dict.html#c.PyDict_GetItemRef) |113| [`PyDict_GetItemString()`](https://python-all.ru/3.13/c-api/dict.html#c.PyDict_GetItemString) | [`PyDict_GetItemStringRef()`](https://python-all.ru/3.13/c-api/dict.html#c.PyDict_GetItemStringRef) |114| [`PyDict_SetDefault()`](https://python-all.ru/3.13/c-api/dict.html#c.PyDict_SetDefault) | [`PyDict_SetDefaultRef()`](https://python-all.ru/3.13/c-api/dict.html#c.PyDict_SetDefaultRef) |115| [`PyDict_Next()`](https://python-all.ru/3.13/c-api/dict.html#c.PyDict_Next) | нет (см. [PyDict\_Next](https://python-all.ru/3.13/howto/free-threading-extensions.html#pydict-next)) |116| [`PyWeakref_GetObject()`](https://python-all.ru/3.13/c-api/weakref.html#c.PyWeakref_GetObject) | [`PyWeakref_GetRef()`](https://python-all.ru/3.13/c-api/weakref.html#c.PyWeakref_GetRef) |117| [`PyWeakref_GET_OBJECT()`](https://python-all.ru/3.13/c-api/weakref.html#c.PyWeakref_GET_OBJECT) | [`PyWeakref_GetRef()`](https://python-all.ru/3.13/c-api/weakref.html#c.PyWeakref_GetRef) |118| [`PyImport_AddModule()`](https://python-all.ru/3.13/c-api/import.html#c.PyImport_AddModule) | [`PyImport_AddModuleRef()`](https://python-all.ru/3.13/c-api/import.html#c.PyImport_AddModuleRef) |119120Не все API, возвращающие заимствованные ссылки, проблематичны. Например, [`PyTuple_GetItem()`](https://python-all.ru/3.13/c-api/tuple.html#c.PyTuple_GetItem) безопасен, так как кортежи неизменяемы. Аналогично, не все варианты использования вышеперечисленных API проблематичны. Например, [`PyDict_GetItem()`](https://python-all.ru/3.13/c-api/dict.html#c.PyDict_GetItem) часто используется для разбора словарей именованных аргументов в вызовах функций; эти словари именованных аргументов фактически являются приватными (недоступны другим потокам), поэтому использование заимствованных ссылок в этом контексте безопасно.121122Некоторые из этих функций были добавлены в Python 3.13. Можно использовать пакет [pythoncapi-compat](https://python-all.ru/3.13/howto/free-threading-extensions.html), чтобы предоставить реализации этих функций для старых версий Python.123124## API выделения памяти125126C API для управления памятью Python предоставляет функции в трёх различных [доменах выделения](https://python-all.ru/3.13/c-api/memory.html#id1): «raw», «mem» и «object». Для потокобезопасности сборка со свободной многопоточностью требует, чтобы только объекты Python выделялись с помощью домена object, и чтобы все объекты Python выделялись с его помощью. Это отличается от предыдущих версий Python, где это было только рекомендацией, а не жёстким требованием.127128> **Примечание**129>130> Найдите использования [`PyObject_Malloc()`](https://python-all.ru/3.13/c-api/memory.html#c.PyObject_Malloc) в своём расширении и проверьте, что выделенная память используется для объектов Python. Используйте [`PyMem_Malloc()`](https://python-all.ru/3.13/c-api/memory.html#c.PyMem_Malloc) для выделения буферов вместо `PyObject_Malloc()`.131132## API состояния потока и GIL133134Python предоставляет набор функций и макросов для управления состоянием потока и GIL, например:135136- [`PyGILState_Ensure()`](https://python-all.ru/3.13/c-api/init.html#c.PyGILState_Ensure) и [`PyGILState_Release()`](https://python-all.ru/3.13/c-api/init.html#c.PyGILState_Release)137- [`PyEval_SaveThread()`](https://python-all.ru/3.13/c-api/init.html#c.PyEval_SaveThread) и [`PyEval_RestoreThread()`](https://python-all.ru/3.13/c-api/init.html#c.PyEval_RestoreThread)138- [`Py_BEGIN_ALLOW_THREADS`](https://python-all.ru/3.13/c-api/init.html#c.Py_BEGIN_ALLOW_THREADS) и [`Py_END_ALLOW_THREADS`](https://python-all.ru/3.13/c-api/init.html#c.Py_END_ALLOW_THREADS)139140Эти функции по-прежнему следует использовать в сборке со свободными потоками для управления состоянием потока, даже когда [GIL](https://python-all.ru/3.13/glossary.html#term-GIL) отключён. Например, если вы создаёте поток вне Python, необходимо вызвать [`PyGILState_Ensure()`](https://python-all.ru/3.13/c-api/init.html#c.PyGILState_Ensure) перед вызовом Python API, чтобы убедиться, что поток имеет корректное состояние потока Python.141142Следует продолжать вызывать [`PyEval_SaveThread()`](https://python-all.ru/3.13/c-api/init.html#c.PyEval_SaveThread) или [`Py_BEGIN_ALLOW_THREADS`](https://python-all.ru/3.13/c-api/init.html#c.Py_BEGIN_ALLOW_THREADS) вокруг блокирующих операций, таких как ввод-вывод или захват блокировок, чтобы позволить другим потокам запустить [циклический сборщик мусора](https://python-all.ru/3.13/glossary.html#term-garbage-collection).143144## Защита внутреннего состояния расширения145146Ваше расширение может иметь внутреннее состояние, которое ранее защищалось GIL. Возможно, потребуется добавить блокировку для защиты этого состояния. Подход будет зависеть от вашего расширения, но некоторые распространённые шаблоны включают:147148- **Кэши**: глобальные кэши – распространённый источник общего состояния. Рассмотрите возможность использования блокировки для защиты кэша или отключите его в сборке со свободными потоками, если кэш не критичен для производительности.149- **Глобальное состояние**: глобальное состояние может потребовать защиты с помощью блокировки или переноса в локальную память потока. В C11 и C++11 для [локальной памяти потока](https://python-all.ru/3.13/howto/free-threading-extensions.html) предусмотрены `thread_local` или `_Thread_local`.150151## Сборка расширений для сборки со свободной многопоточностью152153Расширения C API необходимо собирать специально для сборки со свободной многопоточностью. Колеса (wheels), разделяемые библиотеки и двоичные файлы обозначаются суффиксом `t`.154155- [pypa/manylinux](https://python-all.ru/3.13/howto/free-threading-extensions.html) поддерживает сборку со свободной многопоточностью с суффиксом `t`, например `python3.13t`.156- [pypa/cibuildwheel](https://python-all.ru/3.13/howto/free-threading-extensions.html) поддерживает сборку со свободной многопоточностью, если установить [CIBW\_ENABLE в cpython-freethreading](https://python-all.ru/3.13/howto/free-threading-extensions.html).157158### Ограниченный C API и стабильный ABI159160Сборка со свободной многопоточностью в настоящее время не поддерживает [Ограниченный C API](https://python-all.ru/3.13/c-api/stable.html#limited-c-api) или стабильный ABI. Если вы используете [setuptools](https://python-all.ru/3.13/howto/free-threading-extensions.html) для сборки вашего расширения и в настоящее время устанавливаете `py_limited_api=True`, вы можете использовать `py_limited_api=not sysconfig.get_config_var("Py_GIL_DISABLED")`, чтобы отказаться от ограниченного API при сборке со свободной многопоточностью.161162> **Примечание**163>164> Вам потребуется собрать отдельные колеса специально для сборки со свободной многопоточностью. Если вы в настоящее время используете стабильный ABI, вы можете продолжать собирать одно колесо для нескольких версий Python без свободной многопоточности.165166### Windows167168Из-за ограничения официального установщика Windows вам потребуется вручную определить `Py_GIL_DISABLED=1` при сборке расширений из исходных кодов.169170> **См. также**171>172> [Портирование модулей расширения для поддержки свободной многопоточности](https://python-all.ru/3.13/howto/free-threading-extensions.html): Руководство по портированию, поддерживаемое сообществом, для авторов расширений.173