Обработка встроенных Python вызовов интерпретатора с GIL и многопоточностью - PullRequest
1 голос
/ 22 апреля 2020

Созвездие / Контекст :

Исполняемый файл C ++ (1), который динамически связывает общую библиотеку C ++ emb.so (2), которая, в свою очередь, запускает встроенный интерпретатор python ( 3) который вызывает пользовательские python функции (4).

Встраивание интерпретатора Python (3) происходит с использованием pybind11 . Вызов функции Python из C ++ можно упростить следующим образом:

py::module::import("test").attr("my_func")();

В исполняемом файле (1) есть главный l oop, в котором он может выполнять какую-то другую работу, но он будет вызывать python функция с регулярными интервалами.

Наблюдение :

  • Вариант 1: Если я блокирую внутри функции python, код python выполняется плавно и быстро, но основной исполняемый файл l oop явно заблокирован
  • вариант 2: если я создаю поток python внутри функции python для немедленного возврата из функции, основной исполняемый файл запущен , но код python работает очень медленно (я могу наблюдать итерации for-l oop с печатью одну за другой)

Вопрос :

Почему Вариант 2 такой медленный и как я могу это исправить?

Я предполагаю, что это как-то связано с GIL, и я попытался выпустить GIL в оболочке emb.so, прежде чем вернуться на главную l oop, но я не смог сделать это без gfault.

Есть идеи?

1 Ответ

0 голосов
/ 08 мая 2020

Оказалось, что это очень сильно связано со следующим вопросом:

Встраивание python в многопоточное C приложение

(см. Ответ { ссылка })

Я решил проблему путем явного освобождения GIL после вызова встроенного Python кода, например:

state = PyGILState_Ensure();
// Call Python/C API functions...    
PyGILState_Release(state);

Если вы делаете это в функции или другой области действия C ++, и вы создаете python объекты, вы должны убедиться, что дескриптор объекта python не вызывается после освобождения GIL. Так что не делайте:

void my_func() {
    gil_state = PyGILState_Ensure();
    py::int_ ret = pymodule->attr("GiveMeAnInt")();
    PyGILState_Release(gil_state);
    return ret.cast<int>();
}

, а вместо этого

void my_func() {
    int ret_value;
    gil_state = PyGILState_Ensure();
    {
        py::int_ ret = pymodule->attr("GiveMeAnInt")();
        ret_value = ret.cast<int>();
    }
    PyGILState_Release(gil_state);
    return ret_value;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...