2017-12-28 18 views
0

私はPythonインタプリタを埋め込みたいC++/Qtアプリケーションを持っています。私はPythonをQThreadから呼びたいと思っていますが、PyGILState_Ensure()を呼び出してグローバルインタープリタロック(GIL)を取得しようとすると、デッドロックが発生します。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、Pythonヘッダーとの名前の競合を避けるためです。
  • PyGILState_Ensure()の呼び出し時にスレッドが実行を停止している間、メインスレッドは引き続き禁止されていることに注意してください。 return a.exec();return 0;に変更すると、プログラムは終了します。 (おそらくデッドロックは間違った用語です)
  • 私はPython内からスレッドを作成することには興味がありませんのでご注意ください。私はちょうどQThreadから特定のPythonスクリプトを直接前方に呼びたいと思う。
  • 私はもうsimilarquestionsを読んだことがありますが、そこには微妙な違いがあると感じていましたが、そこにある回答から問題を解決できませんでした。また、PyEval_InitThreads()に電話する推奨事項が混乱しています。Python/C API documentationが正しく理解されていれば、それは必要ないはずです。
+0

:ここ

は、問題を解決main.cppの更新版ですか?アプリケーションがスタックしているときは、デバッガを使用してすべてのスレッドがスタックを呼び出すかどうかを調べます。 –

+0

PyGILState_Ensure()が呼び出された行でデバッガを停止し、そこから1行進むとプログラムがロックアップします。私。スレッドは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(); 
} 
関連する問題