《QThread源码浅析》
《子类化QThread实现多线程》
从往期《QThread源码浅析》可知,在Qt4.4之前,run 是纯虚函数,必须子类化QThread来实现run函数。而从Qt4.4开始,QThread不再支持抽象类,run 默认调用 QThread::exec() ,不需要子类化QThread,只需要子类化一个QObject,通过QObject::moveToThread将QObject派生类移动到线程中即可。这是官方推荐的方法,而且使用灵活、简单、安全可靠。如果线程要用到事件循环,使用继承QObject的多线程方法无疑是一个更好的选择。
这一期主要是说一下,子类化QObject+moveToThread的多线程使用方法以及一些注意问题,其中有很多细节的问题其实和往期《子类化QThread实现多线程》文章是一样的,在这里就不再多说了,不明白的可以到往期《子类化QThread实现多线程》文章找找答案。
写一个继承QObject的类,将需要进行复杂耗时的逻辑封装到槽函数中,作为线程的入口,入口可以有多个;
在旧线程创建QObject派生类对象和QThread对象,最好使用堆分配的方式创建(new),并且最好不要为此两个对象设置父类,便于后期程序的资源管理;
把obj通过moveToThread方法转移到新线程中,此时obj不能有任何的父类;
把线程的finished信号和obj对象、QThread对象的 QObject::deleteLater 槽连接,这个信号槽必须连接,否则会内存泄漏;如果QObject的派生类和QThread类指针是需要重复使用,那么就需要处理由对象被销毁之前立即发出的 QObject::destroyed 信号,将两个指针设置为nullptr,避免出现野指针;
将其他信号与QObject派生类槽连接,用于触发线程执行槽函数里的任务;
初始化完后调用 QThread::start() 来启动线程,默认开启事件循环;
在逻辑结束后,调用 QThread::quit 或者 QThread::exit 退出线程的事件循环。
二、实例写一个继承QObject的类:InheritQObject,代码如下:
#ifndef INHERITQOBJECT_H #define INHERITQOBJECT_H #include <QObject> #include <QThread> #include <QMutex> #include <QMutexLocker> #include <QDebug> class InheritQObject : public QObject { Q_OBJECT public: explicit InheritQObject(QObject *parent = 0) : QObject(parent){ } //用于退出线程循环计时的槽函数 void StopTimer(){ qDebug()<<"Exec StopTimer thread = "<<QThread::currentThreadId(); QMutexLocker lock(&m_lock); m_flag = false; } signals: void ValueChanged(int i); public slots: void QdebugSlot(){ qDebug()<<"Exec QdebugSlot thread = "<<QThread::currentThreadId(); } //计时槽函数 void TimerSlot(){ qDebug()<<"Exec TimerSlot thread = "<<QThread::currentThreadId(); int i=0; m_flag = true; while(1) { ++i; emit ValueChanged(i); QThread::sleep(1); { QMutexLocker lock(&m_lock); if( !m_flag ) break; } } } private: bool m_flag; QMutex m_lock; }; #endif // INHERITQOBJECT_Hmainwindow主窗口类,代码如下:
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include "ui_mainwindow.h" #include "InheritQObject.h" #include <QThread> namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0) : QMainWindow(parent), ui(new Ui::MainWindow){ qDebug()<<"GUI thread = "<<QThread::currentThreadId(); ui->setupUi(this); //创建QThread线程对象以及QObject派生类对象,注意:都不需要设置父类 m_th = new QThread(); m_obj = new InheritQObject(); //改变m_obj的线程依附关系 m_obj->moveToThread(m_th); //释放堆空间资源 connect(m_th, &QThread::finished, m_obj, &QObject::deleteLater); connect(m_th, &QThread::finished, m_th, &QObject::deleteLater); //设置野指针为nullptr connect(m_th, &QObject::destroyed, this, &MainWindow::SetPtrNullptr); connect(m_obj, &QObject::destroyed, this, &MainWindow::SetPtrNullptr); //连接其他信号槽,用于触发线程执行槽函数里的任务 connect(this, &MainWindow::StartTimerSignal, m_obj, &InheritQObject::TimerSlot); connect(m_obj, &InheritQObject::ValueChanged, this, &MainWindow::setValue); connect(this, &MainWindow::QdebugSignal, m_obj, &InheritQObject::QdebugSlot); //启动线程,线程默认开启事件循环,并且线程正处于事件循环状态 m_th->start(); } ~MainWindow(){ delete ui; } signals: void StartTimerSignal(); void QdebugSignal(); private slots: //触发线程执行m_obj的计时槽函数 void on_startBt_clicked(){ emit StartTimerSignal(); } //退出计时槽函数 void on_stopBt_clicked(){ m_obj->StopTimer(); } //检测线程状态 void on_checkBt_clicked(){ if(m_th->isRunning()){ ui->label->setText("Running"); }else{ ui->label->setText("Finished"); } } void on_SendQdebugSignalBt_clicked(){ emit QdebugSignal(); } //退出线程 void on_ExitBt_clicked(){ m_th->exit(0); } //强制退出线程 void on_TerminateBt_clicked(){ m_th->terminate(); } //消除野指针 void SetPtrNullptr(QObject *sender){ if(qobject_cast<QObject*>(m_th) == sender){ m_th = nullptr; qDebug("set m_th = nullptr"); } if(qobject_cast<QObject*>(m_obj) == sender){ m_obj = nullptr; qDebug("set m_obj = nullptr"); } } //响应m_obj发出的信号来改变时钟 void setValue(int i){ ui->lcdNumber->display(i); } private: Ui::MainWindow *ui; QThread *m_th; InheritQObject *m_obj; }; #endif // MAINWINDOW_H