Встраивание Python в программу на C ++, как работать с GIL - PullRequest
1 голос
/ 24 сентября 2019

На работе мы запускаем встроенный интерпретатор Python из программы C ++ в прошивке.Следующая программа показывает суть работы интерпретатора.В действительности это запускает большую программу, но в этом примере показана короткая команда, которая иллюстрирует проблему, с которой мы столкнулись.

#include <Python.h>                                                                                                                                                                                        
#include <stdlib.h>                                                                                                                                                                                        
#include <string>                                                                                                                                                                                          

int main(int argc, char *argv[]){                                                                                                                                                                          
    Py_Initialize();                                                                                                                                                                                         
    PyEval_InitThreads();                                                                                                                                                                                    
    PyThreadState* mainState_ = PyEval_SaveThread();                                                                                                                                                         

    PyEval_AcquireLock();                                                                                                                                                                                    
    PyThreadState* initialState_ = Py_NewInterpreter();                                                                                                                                                      
    assert (initialState_!= 0);                                                                                                                                                                              
    PyThreadState* state_ = PyEval_SaveThread();                                                                                                                                                             

    int result;                                                                                                                                                                                              
    PyEval_RestoreThread(state_);                                                                                                                                                                            
    std::string command = 
        "import numpy as np; a=np.array([2,3]); b=np.array([2,0]); print(a/b)";                                                                                                            
    result = PyRun_SimpleString (command.c_str());                                                                                                                                                           
    state_ = PyEval_SaveThread();                                                                                                                                                                            

    return result;                                                                                                                                                                                           
}

Это можно скомпилировать с помощью

g++ main.cpp -o main $(python2.7-config --cflags --ldflags)

При выполнении это зависает процесс.Кажется, что некоторые библиотеки Python с поддержкой потоков (NumPy, OpenCV) при попытке освободить и повторно получить GIL оказались в тупике.В этом примере мы принудительно делим на ноль в NumPy, который выполняет некоторый специфичный для NumPy код обработки исключений, который включает в себя GIL и который вызывает взаимоблокировку.Если изменить это значение на

#include <Python.h>                                                                                                                                                                                        
#include <stdlib.h>                                                                                                                                                                                        
#include <string>                                                                                                                                                                                          

int main(int argc, char *argv[]){                                                                                                                                                                          

    Py_Initialize();                                                                                                                                                                                         

    int result;                                                                                                                                                                                              
    std::string command = 
        "import numpy as np; a=np.array([2,3]); b=np.array([2,0]); print(a/b)";                                                                                                            
    result = PyRun_SimpleString (command.c_str());                                                                                                                                                           

    return result;                                                                                                                                                                                           
}

, то оно, похоже, выполняется без проблем.

Настоящая программа на C ++ является многопоточной, а исполняемый из нее Python - очень большая программа.Фактическая альтернативная программа (т.е. второй пример) вместо получения GIL перед выполнением Python (как в первом примере) получит универсальную блокировку (мьютекс, семафор), чтобы избежать одновременного доступа.

Яне уверен, что было причиной, чтобы встроить переводчика таким образом.Может ли это изменение в том, как называется интерпретатор Python, быть небезопасным или нарушить работу, например, позволить нескольким потокам одновременно получать доступ к ресурсам, которые должны быть защищены, или это изменение может быть безопасно сделано?

Человека, который это реализовал, больше нет, поэтому я не могу его спросить.

Используемая нами версия Python - 2.7.15, и версии NumPy, в которых это происходит, -1.8.2, 1.15.2, 1.16.5 и версия g ++ 7.4.0.

...