#cpp #qt #многопоточность
Привет всем и снова вопрос про многопоточность, выкладываю исходный код тестового примера. И сразу к вопросу, есть код расчёта который работает в потоке мы можем его принудительно закрыть используя используя флаг stop по нажатию кнопки и в конце функции посылать сигнал emit finished() все работает. Но когда у нас в функции потока идет обращение к функции которая определена в dll которую мы воспринимаем как черный ящик, т.е в потоке идет обращение к ней, в её аргументы идут данные и внутри нее рассчитываются, в самой dll, то как же ее принудительно прервать и удалить поток? внутри нее нет флагов и сигналов о ее прерывании, она работает пока не досчитает. Считает внутри пару минут и мы ждем возвращающее значение и только по выходу из нее наш поток прерывается, а затем удаляется по нашему флагу и сигналу о завершении finished(). И повторюсь, так как прервать принудительно поток? даже если внутри функции dll еще идет расчёт, не дожидаясь возвращающего значения? выкладываю тестовый рабочий пример для ориентира. worker.cpp #include "worker.h" #includeWorker::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 не выполнится, также не произойдёт и освобождение занятых ресурсов. Надо ли говорить, что может натворить сама библиотека, будучи подключенная к какому-нибудь внешнему ресурсу и производящая туда на момент экстренной остановки запись данных. Если причиной немедленного останова потока является такая причина как то, что пользователь не может ждать, то скройте процесс работы соответствующей реакцией в графическом интерфейсе пользователя. Например, если какая-нибудь кнопка предназначена для остановки потока, то пусть она покажет, что всё успешно выполнено, тогда как скрытый таким образом поток прозрачно продолжит свою работу и в конечном итоге завершит её корректно. Если пользователь хочет немедленно выйти из программы, то необязательно его слушаться. Можно просто закрыть окно, не завершая сам процесс, который продолжит свою работу и также корректно завершит её.
Комментариев нет:
Отправить комментарий