Страницы

Поиск по вопросам

понедельник, 6 января 2020 г.

Qt многопоточность, принудительное прерывание потока

#cpp #qt #многопоточность


Привет всем и снова вопрос про многопоточность, выкладываю исходный код тестового
примера. И сразу к вопросу, есть код расчёта который работает в потоке мы можем его
принудительно закрыть используя используя флаг stop по нажатию кнопки и в конце функции
посылать сигнал emit finished() все работает. Но когда у нас в функции потока идет
обращение к функции которая определена в dll которую мы воспринимаем как черный ящик,
т.е в потоке идет обращение к ней, в её аргументы идут данные и внутри нее рассчитываются,
в самой dll, то как же ее принудительно прервать и удалить поток? внутри нее нет флагов
и сигналов о ее прерывании, она работает пока не досчитает. Считает внутри пару минут
и мы ждем возвращающее значение и только по выходу из нее наш поток прерывается, а
затем удаляется по нашему флагу и сигналу о завершении finished(). И повторюсь, так
как прервать принудительно поток? даже если внутри функции dll еще идет расчёт, не
дожидаясь возвращающего значения? выкладываю тестовый рабочий пример для ориентира.

worker.cpp

#include "worker.h"
#include 

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 

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 
#include 

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 
#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 

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();

return a.exec();
}

    


Ответы

Ответ 1



Самым лучшим решением является вариант с модернизацией самой библиотеки с тем, чтобы она принимала и понимала сигналы от управляющего потока. Разумеется, если доступны исходники этой самой библиотеки. В обратном случае, когда контекст выполнения уходит в подключаемую библиотеку, власть над ним теряется, и всё, что возможно сделать, это или резко одёрнуть поводок, применив силу, или терпеливо ждать, когда же тот вернётся в лоно заботливого управления. Применение силы добром оканчивается редко, но если решение принято сознательно и по причине особо острой необходимости, то вполне реально. Для начала необходимо разрешить QThread принудительное и немедленное завершение контролируемого им потока: QThread::setTerminationEnabled(bool enabled = true) Второе, как ни странно, непосредственно само завершение: QThread::terminate() Следует учитывать, что код, который следует за строкой контекста выполнения в методе QThread::run(), после вызова terminate() выполнен не будет. Например: void QThread::run() { myFunc1(); // Контекст выполнения здесь. myFuncInDll(); // Если уничтожить поток, то всё, // что находится ниже выполнено не будет. myFunc2(); emit finished(); } Класс Worker согласно коду в вопросе перемещается в поток, контролируемый QThread. Помимо того, что всё последующее в Worker не выполнится, также не произойдёт и освобождение занятых ресурсов. Надо ли говорить, что может натворить сама библиотека, будучи подключенная к какому-нибудь внешнему ресурсу и производящая туда на момент экстренной остановки запись данных. Если причиной немедленного останова потока является такая причина как то, что пользователь не может ждать, то скройте процесс работы соответствующей реакцией в графическом интерфейсе пользователя. Например, если какая-нибудь кнопка предназначена для остановки потока, то пусть она покажет, что всё успешно выполнено, тогда как скрытый таким образом поток прозрачно продолжит свою работу и в конечном итоге завершит её корректно. Если пользователь хочет немедленно выйти из программы, то необязательно его слушаться. Можно просто закрыть окно, не завершая сам процесс, который продолжит свою работу и также корректно завершит её.

Комментариев нет:

Отправить комментарий