которая решает проблему:

я есть приложение C ++ / Qt, в которое я хочу встроить интерпретатор Python. Я хочу вызвать Python из QThread, но я получаю тупик в строке, где я вызываю PyGILState_Ensure (), чтобы попытаться получить глобальную блокировку интерпретатора (GIL).

Я приведу минимальный и простой пример ниже, который следует рекомендациям, даннымВот:

//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. Я просто хочу прямо вызвать данный скрипт Python из QThread.Я читал другиеаналогичный вопросов, но чувствовал, что рассматриваемые там случаи немного отличаются, и я не смог решить мою проблему из ответов, данных там. Также меня смущают рекомендации звонитьPyEval_InitThreads()что если я понимаюДокументация по API Python / C правильно, не нужно
 Marek R28 дек. 2017 г., 11:49
Откуда ты знаешь, что это мертвый замок? Когда приложение застряло, используйте отладчик для проверки всех потоков вызовов стеков.
 andreasdr28 дек. 2017 г., 13:00
Если я остановлю отладчик в строке, где вызывается PyGILState_Ensure (), и оттуда попытаюсь перейти на одну строку вперед, программа блокируется. То есть поток застревает где-то внутри PyGILState_Ensure ().

Ответы на вопрос(1)

этот ответ было особенно полезно.

Что мне нужно сделать, это позвонитьPyEval_InitThreads() из основного потока (совсем не ясно изочень загадочная документация). Затем, чтобы включитьPyGILState_Ensure() чтобы получить GIL вообще из других потоков (или застрять в бесконечном цикле внутри исходного кода Python, постоянно пытаясь и не получая GIL), мне нужно освободить GIL в основном потоке с помощью вызоваPyEval_SaveThread(), Наконец, я должен снова получить GIL в главном потоке с помощью вызоваPyEval_RestoreThread() (убедившись, что все темы, которые хотят вызвать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();
}

Ваш ответ на вопрос