Py_Finalize在嵌入式Python应用程序中的致命错误 [英] Fatal error during Py_Finalize in embedded Python application

查看:1778
本文介绍了Py_Finalize在嵌入式Python应用程序中的致命错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

感谢您的帮助 - 这个问题的变化已被问过很多次,但我还没有找到一个完整的答案。我将嵌入式Python 3.4.2添加到使用MS MFC类在C ++中编写的现有模拟器工具。该应用程序是多线程的,以便用户可以执行Python脚本并与模拟器系统交互。



如何成功退出?
我是以正确的顺序使用GIL和线程状态命令吗?
我提前结束Python解释器线程并打破Python线程整合机制?



我的问题是当我调用Py_Finalize时,它调用wait_for_thread_shutdown,然后PyThreadState_Get ,并命中一个致命错误,PyThreadState_Get:没有当前线程。基于检测到致命错误的点,它似乎与多线程嵌入式Python应用程序结尾处的线程合并相关。



代码来澄清它,并消除任何似乎不相关的东西。我的道歉,如果我走得太远或不够远。主线程初始化并完成Python。

  BOOL CSimApp :: InitInstance()
{
。 。
//初始化appl的命令表。对象和文档
int iReturn = PyImport_AppendInittab(sim,& PyInit_SimApp);
iReturn = PyImport_AppendInittab(sim_doc,& PyInit_SimDoc);

//初始化Python并准备创建线程
_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解释器线程是响应CMainFrame窗口处理的Windows消息而创建的。 Python线程和解释器响应用户命令运行。当用户完成解释器(Control-Z)时,解释器退出,线程清除并删除Python线程状态,然后线程终止自身。

  void CMainFrame :: OnOpenPythonInterpreter()
{
//创建PyThread线程
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< PyRun_InteractiveLoop中的异常:< e.what()< \\\
;
} catch(...){
std :: cout< Python代码中的异常:UNKNOWN\\\
;
}

PyThreadState_Clear(myThreadState);
PyThreadState_Delete(myThreadState);

:: PostQuitMessage(0);
return 0;
}

int CPyThread :: ExitInstance()
{
return CWinThread :: ExitInstance();
}



在user4815162342的建议下,我修改了我的〜CPyInstance()析构函数在调用Py_Finalize()之前获取GIL。现在我的应用程序似乎正确退出了,谢谢。

  inline CPyInstance ::〜CPyInstance()
{
try {
PyGILState_STATE state = PyGILState_Ensure();
Py_Finalize();
} catch(const std :: exception& e){
safe_cout< Exception in〜CPyInstance():< e.what()< \\\
;
} catch(...){
std :: cout< Python代码中的异常:UNKNOWN\\\
;
}
}


解决方案

正在调用 Py_Finalize ,而不持有全局解释器锁。这是不允许的:对于每个Python API调用,必须保持锁定,只有获得GIL本身的调用有一个例外。



ACQUIRE_PY_GIL RAII guard不适用于此目的,因为它会尝试在 Py_Finalize 返回后释放GIL - 在这种情况下, PyGILState_Ensure 没有匹配的版本。


Thanks for your help with this -- variations on this question have been asked many times, but I haven't found a complete answer. I am adding embedded Python 3.4.2 to an existing simulator tool written in C++ using MS MFC classes. The application is multithreaded so that the user can execute Python scripts and interact with the simulator system.

How do I exit successfully? Am I using the GIL and thread state commands in the proper order? Am I ending the Python interpreter thread prematurely and breaking the Python thread consolidation mechanism?

My problem is that when I call Py_Finalize, it calls wait_for_thread_shutdown, then PyThreadState_Get, and hits a fatal error, "PyThreadState_Get: no current thread." Based on the point where the fatal error is detected, it seems to relate to the consolidation of threads at the end of a multithreaded embedded Python application.

I've condensed my code to clarify it and to eliminate anything that doesn't seem relevant. My apologies if I went too far or not far enough. The main thread initializes and finalizes 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;
    }
    ...
}

I'm using utility classes to create the Python instance (CPyInstance) and to manage the Python GIL (ACQUIRE_PY_GIL). When the application is initialized an instance of CPyInstance is also created. The class CPyInstance initializes and finalizes the Python thread management. Python Global lock management is accomplished with the ACQUIRE_PY_GIL and RELEASE_PY_GIL structures.

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); }
};

The Python interpreter thread is created in response to a Windows message handled by the CMainFrame window. The Python thread and interpreter runs in response to a user command. When the user finishes with the interpreter (Control-Z), the interpreter exits, the thread clears and deletes the Python thread state, and then the thread terminates itself.

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();
}

The CPyThread class actually calls the Python interpreter. When the interpreter returns the GIL is released and the Python thread state is cleared and deleted. The thread terminates in response to the 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: UNKNOWN\n";
    }

    PyThreadState_Clear(myThreadState);
    PyThreadState_Delete(myThreadState);

    ::PostQuitMessage(0);
    return 0;
}

int CPyThread::ExitInstance() 
{
    return CWinThread::ExitInstance();
}

At the suggestion of "user4815162342" I modified my ~CPyInstance() destructor to acquire the GIL before calling Py_Finalize(). Now my application appears to exit properly, thanks.

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: UNKNOWN\n";
    }
}

解决方案

You are calling Py_Finalize without holding the global interpreter lock. This is not allowed: the lock must be held for every Python API call, with the single exception of the call that acquires the GIL itself.

The ACQUIRE_PY_GIL RAII guard is not useful for this purpose, as it would try to release the GIL after Py_Finalize returns — in this case, you must call PyGILState_Ensure without the matching release.

这篇关于Py_Finalize在嵌入式Python应用程序中的致命错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆