Deadlock when QThread tries to acquire Python GIL via PyGILState_Ensure()

后端 未结 1 1244
北海茫月
北海茫月 2021-01-24 16:02

I have a C++/Qt application in which I want to embed the Python interpreter. I want to call Python from a QThread, but I\'m getting a deadlock at the line where I call PyGILStat

1条回答
  •  北恋
    北恋 (楼主)
    2021-01-24 16:43

    After browsing around SO, I've found a solution. Example 1 in this answer was especially helpful.

    What I need to do is to call PyEval_InitThreads() from the main thread (not at all clear from the very cryptic documentation). Then, to enable PyGILState_Ensure() to acquire the GIL at all from other threads (or else get stuck in an infinite loop within the Python source code, continually trying and failing to acquire the GIL), I need to release the GIL in the main thread via a call to PyEval_SaveThread(). Finally, I should retrieve the GIL in the main thread again via a call to PyEval_RestoreThread() (making sure that all threads that want to call PyGILState_Ensure() are definitely finished before that, or else risking a lock again for the same reason as before).

    Here is an updated version of main.cpp which solves the problem:

    #include 
    #include 
    #include "Worker.h"
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
        Py_Initialize();
    
        //Initialize threads and release GIL:
        PyEval_InitThreads();
        PyThreadState *threadState;
        threadState = PyEval_SaveThread();
    
        QThread* thread = new QThread;
        Worker* worker = new Worker();
        worker->moveToThread(thread);
        QObject::connect(thread, SIGNAL(started()), worker, SLOT(process()));
        QObject::connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
        QObject::connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
        QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
        thread->start();
    
        //wait until thread is finished calling Python code:
        thread->wait(1000); //(ugly, but in a proper Qt application, this would be handled differently..)
    
        //Retrieve GIL again and clean up:
        PyEval_RestoreThread(threadState);
        Py_FinalizeEx();
    
        return a.exec();
    }
    

    0 讨论(0)
提交回复
热议问题