Неустранимая ошибка во время Пы завершить во встраиваемых приложений на языке Python
Спасибо за помощь с этим-варианты этого вопроса задавались много раз, но я не нашел полного ответа. Я добавляю встроенный Python 3.4.2 к существующему инструменту симулятора, написанному на C++ с использованием классов MS MFC. Приложение является многопоточным, так что пользователь может выполнять сценарии Python и взаимодействовать с системой симулятора.
Как мне успешно завершить работу?
Я с помощью Джил и состояние потока команд в правильном порядке?
Я заканчиваю интерпретатор Python поток преждевременно и нарушение механизма консолидации потоков Python?
Моя проблема заключается в том, что когда я вызываю Py_Finalize, он вызывает wait_for_thread_shutdown, затем PyThreadState_Get и обнаруживает фатальную ошибку: "PyThreadState_Get: нет текущего потока."Основываясь на точке, где обнаружена фатальная ошибка, она, по-видимому, связана с консолидацией потоков в конце многопоточного встроенного приложения Python.
Я сжал свой код, чтобы прояснить его и исключить все, что это не имеет значения. Мои извинения, если я зашел слишком далеко или недостаточно далеко. Основной поток инициализирует и завершает Python.
BOOL CSimApp::InitInstance()
{
...
// Initialize command table for appl. object and for documents
int iReturn = PyImport_AppendInittab("sim", &PyInit_SimApp);
iReturn = PyImport_AppendInittab("sim_doc", &PyInit_SimDoc);
// Initialize Python and prepar to create threads
_pHInstance = new CPyInstance();
...
}
int CSimApp::ExitInstance()
{
...
if (_pHInstance) {
delete _pHInstance;
_pHInstance = NULL;
}
...
}
Я использую служебные классы для создания экземпляра Python (CPyInstance) и для управления Python GIL (ACQUIRE_PY_GIL). При инициализации приложения также создается экземпляр CPyInstance. Класс CPyInstance инициализирует и завершает управление потоками Python. Управление глобальной блокировкой Python осуществляется с помощью ACQUIRE_PY_GIL и RELEASE_PY_GIL структуры.
class CPyInstance
{
public:
CPyInstance();
~CPyInstance();
static PyThreadState * mainThreadState;
};
inline CPyInstance::CPyInstance()
{
mainThreadState = NULL;
Py_Initialize();
PyEval_InitThreads();
mainThreadState = PyThreadState_Get();
PyEval_ReleaseLock();
}
inline CPyInstance::~CPyInstance()
{
Py_Finalize();
}
static CPyInstance *_pHInstance = NULL;
int PyExit()
{
if (_pHInstance) {
delete _pHInstance;
_pHInstance = NULL;
}
return 0;
}
struct ACQUIRE_PY_GIL {
PyGILState_STATE state;
ACQUIRE_PY_GIL() { state = PyGILState_Ensure(); }
~ACQUIRE_PY_GIL() { PyGILState_Release(state); }
};
struct RELEASE_PY_GIL {
PyThreadState *state;
RELEASE_PY_GIL() { state = PyEval_SaveThread(); }
~RELEASE_PY_GIL() { PyEval_RestoreThread(state); }
};
Поток интерпретатора Python создается в ответ на сообщение Windows, обрабатываемое окном CMainFrame. Поток Python и интерпретатор выполняются в ответ на команду пользователя. Когда пользователь завершает работу с интерпретатором (Control-Z), интерпретатор завершает работу, поток очищает и удаляет состояние потока Python, а затем поток завершает сам себя.
void CMainFrame::OnOpenPythonInterpreter()
{
// Create PyThread thread
m_pPyThread = (CPyThread*)AfxBeginThread(RUNTIME_CLASS(CPyThread),
THREAD_PRIORITY_BELOW_NORMAL,0, CREATE_SUSPENDED);
CMainFrame* mf = (CMainFrame*)theApp.m_pMainWnd;
m_pPyThread->SetOwner(this,((CWnd*)mf)->GetSafeHwnd());
m_pPyThread->CreateLocks(&m_PyThreadEvent,&m_PyThreadBusyMutex);
m_pPyThread->ResumeThread();
}
Класс CPyThread фактически вызывает интерпретатор Python. Когда переводчик возвращает GIL освобождается и состояние потока Python очищается и удаляется. Поток завершается в ответ на PostQuitMessage.
int CPyThread::Run()
{
PyEval_AcquireLock();
PyInterpreterState * mainInterpreterState = CPyInstance::mainThreadState->interp;
PyThreadState * myThreadState = PyThreadState_New(mainInterpreterState);
PyEval_ReleaseLock();
try {
ACQUIRE_PY_GIL lock;
FILE* fp1 = stdin;
char *filename = "Embedded";
PyRun_InteractiveLoop(fp1, filename);
} catch(const std::exception &e) {
safe_cout << "Exception in PyRun_InteractiveLoop: " << e.what() << "n";
} catch(...) {
std::cout << "Exception in Python code: UNKNOWNn";
}
PyThreadState_Clear(myThreadState);
PyThreadState_Delete(myThreadState);
::PostQuitMessage(0);
return 0;
}
int CPyThread::ExitInstance()
{
return CWinThread::ExitInstance();
}
По предложению "user4815162342" я изменил свой деструктор ~CPyInstance (), чтобы получить GIL перед вызовом Py_Finalize (). Теперь мое приложение, кажется, завершается должным образом, спасибо.
inline CPyInstance::~CPyInstance()
{
try {
PyGILState_STATE state = PyGILState_Ensure();
Py_Finalize();
} catch(const std::exception &e) {
safe_cout << "Exception in ~CPyInstance(): " << e.what() << "n";
} catch(...) {
std::cout << "Exception in Python code: UNKNOWNn";
}
}
1 ответ:
Вы вызываете
Py_Finalize, не удерживая глобальную блокировку интерпретатора. Это недопустимо: блокировка должна удерживаться для каждого вызова API Python, за единственным исключением вызова, который получает сам GIL.RAII guard не полезен для этой цели, так как он попытается освободить GIL после возврата
Py_Finalize- в этом случае вы должны вызватьPyGILState_Ensureбез соответствующего освобождения.
Comments