Как правильно остановить не-l oop QThread? - PullRequest
0 голосов
/ 18 января 2020

Введение

Допустим, у меня есть приложение с GUI, которое собирает некоторые данные от пользователя и затем вызывает встроенный сценарий python. Я хочу добавить «кнопку отмены» в случае, если пользователь хочет остановить процесс.

Примерный код

mainwindow

#include "calc_script.h"

signals:
    void stopWorkSignal();

private:
    calc_script *sender;

private slots:
    Calculating()
    on_pushButton_Cancel_clicked()



void MainWindow::Calculating()
{
QThread* newThread = new QThread();
connect(newThread, &QThread::started,
        [=]() { sender->transfer(val_1, val_2, val_3); });
connect(this,
    SIGNAL(stopWorkSignal()),
    newThread,
    SLOT(deleteLater())
newThread->start();
}

void MainWindow::on_pushButton_Cancel_clicked()
{
    emit stopWorkSignal();
    qDebug() << "stopwork signal emmitted";
}

calc_script.cpp

void calc_script::transfer(double val_1, double val_2, double val_3)
{
///Here the python (from boost.python) is executed
    while(1) {}//this loop will generate a load to mimic this script, you cannot edit it, as the communication with .py is one-side at this lvl
}

Проблема Когда вызывается сигнал, я получаю ошибку QThread destroyed while thread is still running (и кажется, что вычисления все еще продолжаются). Если я пройду SLOT(quit()), ничего не произойдет. Если бы вычисление было простым l oop, я мог бы передать флаг, чтобы затормозить l oop. Но из-за вызова скрипта python я не могу этого сделать, поэтому я пытаюсь уничтожить поток, содержащий вычисления. Как правильно сделать описанную функциональность?

PS. Я знаю, что не включил весь вызов python, но он очень длинный. Для ошибки воспроизведения вы можете использовать любые не-l oop длинные вычисления внутри функции transfer, в основном это будет та же самая ситуация.

1 Ответ

2 голосов
/ 19 января 2020

Вы не можете принудительно прекратить поток; все, что вы можете сделать, это попросить его выйти, а затем ждать, пока он выйдет сам по себе. (существует метод QThread :: terminate () , но вы не должны использовать его в производственном коде, так как это вызовет проблемы: например, если поток заблокировал мьютекс в тот момент, когда он После завершения работы мьютекс останется заблокированным навсегда, и ваша программа будет заблокирована и зависнет при следующей попытке заблокировать этот мьютекс).

Таким образом, у вас есть два варианта: либо найти способ задать Python завершить поток или использовать объект QProcess (или что-то эквивалентное ему) для запуска кода Python в дочернем процессе, а не внутри потока. Преимущество выполнения кода Python в отдельном процессе состоит в том, что вы можете безопасно kill () дочерний процесс - поскольку дочерний процесс не имеет общего состояния с вашим GUI процессом, и ОС автоматически очистит все ресурсы, выделенные дочерним процессом, нет проблем с тем, что дочерний процесс оставляет мьютексы заблокированными или другие ресурсы не освобождаются.

Если вы предпочитаете задать поток Python ( или обработать) вежливо выйти, вместо того, чтобы просто ударить молотком по нему, это можно сделать через сетевой интерфейс; например, вы можете создать TCP-соединение между вашим кодом GUI и событием Python l oop, а событие Python l oop может периодически выполнять неблокирующее чтение в конце TCP подключение. Затем, когда ваш GUI хочет, чтобы Python l oop вышел, GUI может закрыть свой сокет TCP, и это приведет к тому, что вызов Python l oop к read() вернет 0 (он же EOF), который Python l oop знал бы, означает «время для выхода», поэтому он мог бы выйти добровольно.

...