Подтвердить что ты не робот

Concurrency: Являются ли расширения Python, написанные на C/С++, затронутыми глобальным блокировщиком Interpreter?

Одной из самых сильных точек Python является удобство написания расширений C и С++ для ускорения обработки процессором частей кода. Могут ли эти расширения избежать блокировки Global Interpreter или они также ограничены GIL? Если нет, то эта "простота расширения" является еще большей функцией убийцы, чем я ранее понял. Я подозреваю, что ответ не является простым да-или-нет, но я не уверен, поэтому я задаю этот вопрос здесь, в StackOverflow.

4b9b3361

Ответ 1

Да, вызовы на C-расширения (подпрограммы C, вызываемые из Python) по-прежнему подлежат GIL.

Однако вы можете вручную освободить GIL внутри своего расширения C до тех пор, пока вы будете осторожны, чтобы повторно подтвердить его, прежде чем возвращать управление виртуальной машине Python.

Для получения информации см. макросы Py_BEGIN_ALLOW_THREADS и Py_END_ALLOW_THREADS: http://docs.python.org/c-api/init.html#thread-state-and-the-global-interpreter-lock

Ответ 3

Расширения C/С++ для Python не связаны GIL. Однако вам действительно нужно знать, что вы делаете. Из http://docs.python.org/c-api/init.html:

Блокировка глобального интерпретатора используется для защиты указателя на текущее состояние потока. При освобождении блокировки и сохранении состояния потока текущий указатель состояния потока должен быть извлечен до освобождения блокировки (так как другой поток может немедленно получить блокировку и сохранить свое собственное состояние потока в глобальной переменной). И наоборот, при приобретении блокировки и восстановлении состояния потока блокировка должна быть получена перед сохранением указателя состояния потока.

Почему я так подробно рассказываю об этом? Поскольку, когда потоки создаются из C, они не имеют блокировки глобального интерпретатора и не имеют структуры данных состояния потока для них. Такие потоки должны загружаться самим собой, сначала создавая структуру данных состояния потока, затем приобретая блокировку и, наконец, сохраняя свой указатель состояния потока, прежде чем они смогут начать использовать API Python/C. Когда они будут выполнены, они должны reset указатель состояния потока, отпустить блокировку и, наконец, освободить структуру данных состояния потока.

Ответ 4

Проверьте Cython, он имеет аналогичный синтаксис для Python, но с несколькими конструкциями типа "cdef", быстрыми функциями доступа numpy и оператором "с nogil" (который делает то, что он говорит).

Ответ 5

Если вы пишете свое расширение на С++, вы можете использовать RAII для легкого и четкого написания кода, управляющего GIL. Я использую эту пару RAII-структур:

namespace py {

    namespace gil {

        struct release {
            PyThreadState* state;
            bool active;

            release()
                :state(PyEval_SaveThread()), active(true)
                {}

            ~release() { if (active) { restore(); } }

            void restore() {
                PyEval_RestoreThread(state);
                active = false;
            }
        };

        struct ensure {
            PyGILState_STATE* state;
            bool active;

            ensure()
                :state(PyGILState_Ensure()), active(true)
                {}

            ~ensure() { if (active) { restore(); } }

            void restore() {
                PyGILState_Release(state);
                active = false;
            }
        };

    }

}

... позволяя GIL переключаться для данного блока (семантическим образом, который может казаться смутно знакомым для любых фанатов Pythonista-менеджера контекста):

PyObject* YourPythonExtensionFunction(PyObject* self, PyObject* args) {

    Py_SomeCAPICall(…);     /// generally, if it starts with Py* it needs the GIL
    Py_SomeOtherCall(…);    /// ... there are exceptions, see the docs

    {
        py::gil::release nogil;
        std::cout << "Faster and less block-y I/O" << std::endl
                  << "can run inside this block -" << std::endl
                  << "unimpeded by the GIL";
    }

    Py_EvenMoreAPICallsForWhichTheGILMustBeInPlace(…);

}

... В самом деле, лично я также обнаружил легкость распространения Python, а уровень контроля над внутренними структурами и состоянием - это функция убийцы.