【QT】子类化QThread实现多线程 (4)

MainWindow类添加ExitBt、TerminateBt两个按钮,用于调用WorkerTh->exit(0)、WorkerTh->terminate()退出线程函数。由往期《QThread源码浅析》文章中《QThread::quit()、QThread::exit()、QThread::terminate()源码》小节得知调用quit和exit是一样的,所以本处只添加了ExitBt按钮:

#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include "ui_mainwindow.h" #include "InheritQThread.h" #include <QThread> #include <QDebug> namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr) : QMainWindow(parent), ui(new Ui::MainWindow){ qDebug()<<"GUI thread = "<<QThread::currentThreadId(); ui->setupUi(this); WorkerTh = new InheritQThread(); connect(WorkerTh, &InheritQThread::ValueChanged, this, &MainWindow::setValue); connect(this, &MainWindow::QdebugSignal, WorkerTh, &InheritQThread::QdebugSlot); } ~MainWindow(){ delete ui; } signals: void QdebugSignal(); public slots: void setValue(int i){ ui->lcdNumber->display(i); } private slots: void on_startBt_clicked(){ WorkerTh->start(); } void on_stopBt_clicked(){ WorkerTh->StopThread(); } void on_checkBt_clicked(){ if(WorkerTh->isRunning()){ ui->label->setText("Running"); }else{ ui->label->setText("Finished"); } } void on_SendQdebugSignalBt_clicked(){ emit QdebugSignal(); } void on_ExitBt_clicked(){ WorkerTh->exit(0); } void on_TerminateBt_clicked(){ WorkerTh->terminate(); } private: Ui::MainWindow *ui; InheritQThread *WorkerTh; }; #endif // MAINWINDOW_H

运行上述的例程,点击【start】启动线程按钮,然后直接点击【exit(0)】或者【terminate()】,这样会直接退出线程吗?
点击【exit(0)】按钮(猛点)

【QT】子类化QThread实现多线程

点击【terminate()】按钮(就点一点)

【QT】子类化QThread实现多线程

由上述情况我们可以看到上面例程的线程启动之后,无论怎么点击【start】按钮,线程都不会退出,点击【terminate()】按钮的时候就会立刻退出当前线程。由往期《QThread源码浅析》文章中《QThread::quit()、QThread::exit()、QThread::terminate()源码》小节可以得知,若使用QThread::quit()、QThread::exit()来退出线程,该线程就必须要在事件循环的状态(也就是正在执行exec()),线程才会退出。而QThread::terminate()不管线程处于哪种状态都会强制退出线程,但这个函数存在非常多不安定因素,不推荐使用。我们下面来看看如何正确退出线程。

(1)如何正确退出线程?

如果线程内没有事件循环,那么只需要用一个标志变量来跳出run函数的while循环,这就可以正常退出线程了。

如果线程内有事件循环,那么就需要调用QThread::quit()或者QThread::exit()来结束事件循环。像刚刚举的例程,不仅有while循环,循环后面又有exec(),那么这种情况就需要先让线程跳出while循环,然后再调用QThread::quit()或者QThread::exit()来结束事件循环。如下:

【QT】子类化QThread实现多线程

注意:尽量不要使用QThread::terminate()来结束线程,这个函数存在非常多不安定因素。

(2)如何正确释放线程资源?

退出线程不代表线程的资源就释放了,退出线程只是把线程停止了而已,那么QThread类或者QThread派生类的资源应该如何释放呢?直接 delete QThread类或者派生类的指针吗?当然不能这样做,千万别手动delete线程指针,手动delete会发生不可预料的意外。理论上所有QObject都不应该手动delete,如果没有多线程,手动delete可能不会发生问题,但是多线程情况下delete非常容易出问题,那是因为有可能你要删除的这个对象在Qt的事件循环里还排队,但你却已经在外面删除了它,这样程序会发生崩溃。 线程资源释放分为两种情况,一种是在创建QThread派生类时,添加了父对象,例如在MainWindow类中WorkerTh = new InheritQThread(this)让主窗体作为InheritQThread对象的父类;另一种是不设置任何父类,例如在MainWindow类中WorkerTh = new InheritQThread()。

1、创建QThread派生类,有设置父类的情况:

这种情况,QThread派生类的资源都让父类接管了,当父对象被销毁时,QThread派生类对象也会被父类delete掉,我们无需显示delete销毁资源。但是子线程还没结束完,主线程就destroy掉了(WorkerTh的父类是主线程窗口,主线程窗口如果没等子线程结束就destroy的话,会顺手把WorkerTh也delete这时就会奔溃了)。 注意:这种情况不能使用moveToThread(this)改变对象的依附性。 因此我们应该把上面MainWindow类的构造函数改为如下:

~MainWindow(){ WorkerTh->StopThread();//先让线程退出while循环 WorkerTh->exit();//退出线程事件循环 WorkerTh->wait();//挂起当前线程,等待WorkerTh子线程结束 delete ui; }

2、创建QThread派生类,没有设置父类的情况:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpdfzd.html