Question:
Hello everyone and again a question about multithreading, I post the source code of the test case. And immediately to the question, there is a calculation code that works in the stream, we can forcibly close it using the stop flag when the button is pressed and at the end of the function send the emit finished () signal, everything works. But when we call a function in the thread function that is defined in the dll which we perceive as a black box, i.e. the thread calls it, data goes into its arguments and is calculated inside it, in the dll itself, then how can it force abort and delete the thread? inside it there are no flags and signals about its interruption, it works until it counts. It counts inside a couple of minutes and we are waiting for the return value, and only upon exiting it, our thread is interrupted, and then removed by our flag and the finished () completion signal. And I repeat, since how to interrupt the flow forcibly? even if there is still a calculation inside the dll function, without waiting for the return value? I post a test working example for reference.
worker.cpp
#include "worker.h"
#include <QDebug>
Worker::Worker(QObject *parent) : QObject(parent)
{
Stop = false;
temp = 0;
}
Worker::~Worker()
{
qDebug() << "destruction Thread";
}
void Worker::process()
{
Stop = false;
if(!Stop == true)
{
for (; temp <= 100000; temp++)
{
if(!Stop == true)
{
emit(sendNumber(temp));
qDebug() << temp;
// ЗДЕСЬ БУДЕТ ВЫЗЫВАТСЯ ФУНКЦИЯ ИЗ DLL
// в её аргументы будут поступать данные и внутри обрабатываться очень долго
// до 2-3 минут обработки и возвращать значения
// но нам нужно сразу ее прервать, даже если она еще внутри расчитывает
}
else
{
return;
}
}
}
emit finished(); // вызывается при завершении расчёта
}
void Worker::reciveBoolStop(bool Numb)
{
Stop = Numb;
qDebug() << "reciveBoolStop = " << Stop;
emit finished(); // вызывается при отмене расчёта
}
worker.h
#ifndef WORKER_H
#define WORKER_H
#include <QObject>
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker(QObject *parent = 0);
~Worker();
bool Stop;
int temp;
signals:
void finished();
//void error(QString err);
void sendNumber(int);
public slots:
void process();
void reciveBoolStop(bool Numb);
};
#endif // WORKER_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QtCore>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(this->ui->pushButton_start, SIGNAL(clicked()), this, SLOT(startGUI()));
connect(this->ui->pushButton_stop, SIGNAL(clicked()), this, SLOT(stopGUI()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::startGUI()
{
// Создание потока
QThread* thread = new QThread;
Worker* worker = new Worker();
// Передаем права владения "рабочим" классом, классу QThread.
worker->moveToThread(thread);
// Соединяем сигнал started потока, со слотом process "рабочего" класса, т.е. начинается выполнение нужной работы.
connect(thread, SIGNAL(started()), worker, SLOT(process()));
// Отображаем в главном потоке Gui, значения из вторичного потока
connect(worker, SIGNAL(sendNumber(int)), this, SLOT(LineEditUi(int)));
// Оповещаем поток, что нужно остановиться
connect(this, SIGNAL(sendNumberBoolStop(bool)), worker, SLOT(reciveBoolStop(bool)), Qt::DirectConnection);
// ВЫЗЫВАЕТ УТЕЧКУ ПАМЯТИ
//connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
// По завершению выходим из потока, и удаляем рабочий класс
connect(worker, SIGNAL(destroyed(QObject*)), thread, SLOT(quit())); // ТАК ПРАВИЛЬНО
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
// Удаляем поток, после выполнения операции
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
}
void MainWindow::LineEditUi(int number)
{
ui->lineEdit->setText(QString::number(number));
}
void MainWindow::stopGUI()
{
Stop = true;
qDebug() << Stop;
sendNumberBoolStop(Stop);
qDebug() << "sendMumberBoolStop = " << Stop;
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "worker.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
bool Stop;
public slots:
void startGUI();
void stopGUI();
void LineEditUi(int number);
signals:
void sendNumberBoolStop(bool);
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
Answer:
The best solution is to upgrade the library itself so that it accepts and understands signals from the control flow. Of course, if the sources of this library are available. Otherwise, when the execution context goes to the link library, power over it is lost, and all that can be done is either to sharply pull the leash, using force, or to patiently wait for it to return to the bosom of caring management.
The use of force rarely ends in good, but if the decision is made consciously and because of a particularly urgent need , then it is quite real.
The first step is to allow QThread
to force and immediately terminate the thread it controls:
QThread::setTerminationEnabled(bool enabled = true)
The second, oddly enough, is the end itself:
QThread::terminate()
Note that code that follows the execution context string in the QThread::run()
method will not be executed after terminate()
is called. For instance:
void QThread::run() {
myFunc1();
// Контекст выполнения здесь.
myFuncInDll();
// Если уничтожить поток, то всё,
// что находится ниже выполнено не будет.
myFunc2();
emit finished();
}
The Worker
class, according to the code in the question, is moved to a thread controlled by QThread
. In addition to the fact that everything that follows in the Worker
will not be executed, the release of occupied resources will also not occur. Needless to say, what the library itself can do when it is connected to some external resource and records data there at the time of an emergency stop.
If the reason for stopping the thread immediately is something like the user can't wait, then hide the process with an appropriate response in the graphical user interface.
For example, if a button is meant to stop a thread, then let it show that everything was completed successfully, while the thread hidden in this way will transparently continue its work and eventually complete it gracefully.
If the user wants to immediately exit the program, then it is not necessary to obey him. You can simply close the window without terminating the process itself, which will continue its work and also correctly complete it.