我有一个C ++ / Qt应用程序,我想在其中嵌入Python解释器。我想从QThread调用Python,但是我在调用PyGILState_Ensure()的行中遇到死锁,以便尝试获取全局解释器锁(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
其他一些评论:
CONFIG += no_keywords
,以避免名称与Python标题冲突。return a.exec();
更改为return 0;
,程序将退出。 (因此,使用死锁是错误的术语。)PyEval_InitThreads()
的建议感到困惑,如果我理解正确questions,则不应该这样做。答案 0 :(得分:0)
浏览了SO之后,我找到了解决方案。 this answer中的示例1特别有用。
我需要做的是从主线程中调用PyEval_InitThreads()
(the very cryptic documentation完全不清楚)。然后,要使PyGILState_Ensure()
从其他线程中获取GIL(或者在Python源代码中陷入无限循环,不断尝试并且无法获取GIL),我需要释放GIL主线程通过调用PyEval_SaveThread()
。最后,我应该通过调用PyEval_RestoreThread()
再次检索主线程中的GIL(确保所有想要调用PyGILState_Ensure()
的线程在此之前完成,否则再次冒险锁定和以前一样的原因)。
以下是解决此问题的main.cpp
的更新版本:
#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();
}