На работе мы запускаем встроенный интерпретатор 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.