2017-12-28 71 views
0

나는 Python 인터프리터를 포함시키고 자하는 C++/Qt 응용 프로그램을 가지고 있습니다. QThread에서 파이썬을 호출하고 싶지만 전역 인터프리터 잠금 (GIL)을 얻기 위해 PyGILState_Ensure()를 호출하는 라인에서 교착 상태가 발생합니다.QThread가 PyGILState_Ensure()를 통해 Python GIL을 얻으려고 할 때 교착 상태가 발생합니다.

내가 here 주어진 권고 다음, 이는 아래의 최소 똑바로 앞으로 예제를 제공합니다

:

//main.cpp: 
#include <QCoreApplication> 
#include <QThread> 
#include "Worker.h" 

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

int main(int argc, char *argv[]) 
{ 
    QCoreApplication a(argc, argv); 
    Py_Initialize(); 
    startThread(); 
    Py_FinalizeEx(); 
    return a.exec(); 
} 


//Worker.h: 
#ifndef WORKER_H 
#define WORKER_H 

#include <QObject> 
#include "Python.h" 

class Worker : public QObject 
{ 
    Q_OBJECT 
public: 
    explicit Worker(QObject *parent = nullptr) : QObject(parent) {} 

Q_SIGNALS: 
    void finished(); 

public Q_SLOTS: 
    void process() 
    { 
     qDebug("Calling Python"); 
     PyGILState_STATE gstate = PyGILState_Ensure(); 
     PyRun_SimpleString("print(\"hello\")"); 
     PyGILState_Release(gstate); 
     qDebug("Done calling Python"); 
     Q_EMIT finished(); 
    } 
}; 

#endif // WORKER_H 

일부 추가 의견 :

  • 참고 : .PRO 파일이 라인을 포함을 CONFIG += no_keywords, 파이썬 헤더와 이름 충돌을 피하기 위해.
  • 스레드가 PyGILState_Ensure()를 호출 할 때 실행을 중지하는 동안 주 스레드는 계속 제한되지 않는다는 점에 유의해야합니다. return a.exec();return 0;으로 변경하면 프로그램이 종료됩니다. (교착 상태는 사용하기에 잘못된 용어입니다.)
  • 파이썬에서 스레드를 만드는 데 관심이 없습니다. QThread에서 주어진 Python 스크립트를 직접적으로 순차적으로 호출하고자합니다.
  • 나는 다른 similarquestions을 읽었지만, 거기에는 미묘한 차이가 있다고 생각되어 거기에서 주어진 답에서 나의 문제를 해결할 수 없었다. 또한, 내가 Python/C API documentation을 올바르게 이해한다면, 필요하지 않아야하는 PyEval_InitThreads()으로 전화를 걸 것을 권고하는 것에 혼란 스럽습니다.
+0

어떻게 당신이 죽은 잠금입니다 알고 :

여기

문제를 해결 main.cpp의 업데이트 된 버전은? 응용 프로그램이 작동하지 않을 때 디버거를 사용하여 모든 스레드 호출 스택을 검사합니다. –

+0

PyGILState_Ensure()가 호출 된 줄에서 디버거를 멈추고 거기에서 한 줄 앞으로 나가면 프로그램이 잠 깁니다. 나는. 스레드가 PyGILState_Ensure() 내 어딘가에 갇히게됩니다. – andreasdr

답변

0

SO 주변을 탐색 한 후에 해결책을 찾았습니다. this answer의 예 1이 특히 유용했습니다.

내가해야 할 일은 PyEval_InitThreads()을 주 스레드에서 호출하는 것입니다 (전혀 the very cryptic documentation에서 알 수 없음). 그런 다음 PyGILState_Ensure()이 다른 스레드에서 GIL을 전혀 얻지 못하게하려면 (또는 Python 소스 코드 내에서 무한 루프에 걸려 계속 GIL을 얻으려고 시도하는 경우) GIL을 통해 주 스레드에서 해제해야합니다. PyEval_SaveThread()으로 전화하십시오. 마지막으로 PyEval_RestoreThread() (이전에 PyGILState_Ensure()을 호출하려는 모든 스레드가 확실히 완료되었는지 확인하거나 이전과 같은 이유로 다시 잠길 위험이 있음을 확인하여 주 스레드에서 GIL을 다시 검색해야합니다.

#include <QCoreApplication> 
#include <QThread> 
#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(); 
}