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

QThread源码浅析》

子类化QThread来实现多线程, QThread只有run函数是在新线程里的,其他所有函数都在QThread生成的线程里。正确启动线程的方法是调用QThread::start()来启动,如果直接调用run成员函数,这个时候并不会有新的线程产生( 原因: 可以查看往期《QThread源码浅析》文章,了解下run函数是怎么被调用的)。

一、步骤

子类化 QThread;

重写run,将耗时的事件放到此函数执行;

根据是否需要事件循环,若需要就在run函数中调用 QThread::exec() ,开启线程的事件循环。事件循环的作用可以查看往期《QThread源码浅析》文章中《QThread::run()源码》小节进行阅读;

为子类定义信号和槽,由于槽函数并不会在新开的线程运行,所以需要在构造函数中调用 moveToThread(this)。 注意:虽然调用moveToThread(this)可以改变对象的线程依附性关系,但是QThread的大多数成员方法是线程的控制接口,QThread类的设计本意是将线程的控制接口供给旧线程(创建QThread对象的线程)使用。所以不要使用moveToThread()将该接口移动到新创建的线程中,调用moveToThread(this)被视为不好的实现。

接下来会通过 使用线程来实现计时器,并实时在UI上显示 的实例来说明不使用事件循环和使用事件循环的情况。(此实例使用QTimer会更方便,此处为了说明QThread的使用,故使用线程来实现)

二、不使用事件循环实例

InheritQThread.hpp

class InheritQThread:public QThread {     Q_OBJECT public:     InheritQThread(QObject *parent = Q_NULLPTR):QThread(parent){              }     void StopThread(){         QMutexLocker lock(&m_lock);         m_flag = false;     } protected:     //线程执行函数     void run(){         qDebug()<<"child 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;             }         }     } signals:     void ValueChanged(int i); public:     bool m_flag;     QMutex m_lock; };

mainwindow.hpp

class MainWindow : public QMainWindow {     Q_OBJECT public:     explicit MainWindow(QWidget *parent = nullptr) :         QMainWindow(parent),         ui(new Ui::MainWindow){         ui->setupUi(this); qDebug()<<"GUI thread = "<<QThread::currentThreadId();         WorkerTh = new InheritQThread(this);         connect(WorkerTh, &InheritQThread::ValueChanged, this, &MainWindow::setValue);     }     ~MainWindow(){         delete ui;     } 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");         }     } private:     Ui::MainWindow *ui;     InheritQThread *WorkerTh; };

在使用多线程的时候,如果出现共享资源使用,需要注意资源抢夺的问题,例如上述InheritQThread类中m_flag变量就是一个多线程同时使用的资源,上面例子使用 QMutexLocker+QMutex 的方式对临界资源进行安全保护使用,其实际是使用了 RAII技术:(Resource Acquisition Is Initialization),也称为“资源获取就是初始化”,是C++语言的一种管理资源、避免泄漏的惯用法。C++标准保证任何情况下,已构造的对象最终会销毁,即它的析构函数最终会被调用。简单的说,RAII 的做法是使用一个对象,在其构造时获取资源,在对象生命期控制对资源的访问使之始终保持有效,最后在对象析构的时候释放资源。具体 QMutexLocker+QMutex 互斥锁的原理以及使用方法,在这里就不展开说了,这个知识点网上有很多非常好的文章。

效果:

(1)在不点【start】按键的时候,点击【check thread state】按钮检查线程状态,该线程是未开启的。

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

(2)按下【start】后效果如下,并查看终端消息打印信息:

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

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