QT 关于使用QThread实现多线程(重写run函数)

心已赠人 2023-07-20 05:39 53阅读 0赞

在QT中,有两种多线程的方法,一种是继承QThread的run函数,另一种是把一个继承于QObject的类转移到一个Thread里。

Qt4.8之前都是使用继承QThread的run这种方法(本次主要介绍的就是这种),其主要步骤如下:

0x01、新建一个类(WorkThread),把该类的父类改为QThread

0x02、重写QThread的run函数,在run函数中搞事情(耗时操作)

0x03、在需要使用的地方,new一个子线程,然后调用QThread的 start 方法启动线程。

0x04、如果需要结束线程,则使用QThread的 wait 方法。

示例代码如下(workthread为子线程,maindialog为调用者):

workthread.h

  1. #ifndef WORKTHREAD_H
  2. #define WORKTHREAD_H
  3. #include <QObject>
  4. #include <QThread>
  5. class WorkThread : public QThread/*QObject*/
  6. {
  7. Q_OBJECT
  8. public:
  9. explicit WorkThread(QObject *parent = 0);
  10. signals:
  11. void currentNum(int);
  12. void workDone();
  13. public slots:
  14. void stopThread();
  15. protected:
  16. virtual void run();
  17. private:
  18. volatile bool bStop = false;//易失性变量,用volatile进行申明
  19. };
  20. #endif // WORKTHREAD_H

workthread.cpp

  1. #include "workthread.h"
  2. #include <QDebug>
  3. #include <QMessageBox>
  4. WorkThread::WorkThread(QObject *parent) : QThread(parent)
  5. {
  6. }
  7. void WorkThread::run()
  8. {
  9. //注意:在子线程中不能出现跟UI相关的代码(QMessageBox这些都不行)
  10. //ASSERT failure in QWidget: "Widgets must be created in the GUI thread.",
  11. //QMessageBox::information(NULL, "info", "I'm sub thread");
  12. //do something...
  13. for(int i=0; i<10; i++)
  14. {
  15. if(bStop == false)
  16. {
  17. QThread::msleep(500);
  18. qDebug() << "SubThreadId:" << QThread::currentThreadId() << "i:" << i;
  19. emit currentNum(i);
  20. }
  21. }
  22. emit workDone();
  23. }
  24. void WorkThread::stopThread()
  25. {
  26. bStop = true;
  27. }

maindialog.h

  1. #ifndef MAINDIALOG_H
  2. #define MAINDIALOG_H
  3. #include <QDialog>
  4. class MainDialog : public QDialog
  5. {
  6. Q_OBJECT
  7. public:
  8. MainDialog(QWidget *parent = 0);
  9. ~MainDialog();
  10. public slots:
  11. void onCurrentNum(int);
  12. void onWorkDone();
  13. };
  14. #endif // MAINDIALOG_H

maindialog.cpp

  1. #include "maindialog.h"
  2. #include "workthread.h"
  3. #include <QPushButton>
  4. #include <QDebug>
  5. MainDialog::MainDialog(QWidget *parent)
  6. : QDialog(parent)
  7. {
  8. //创建"开始"按钮 按照控件的大小创建窗口
  9. QPushButton * btnStart = new QPushButton("Start", this);
  10. //移动btnStart按钮
  11. btnStart->move(100,100);
  12. //调整按钮大小
  13. btnStart->resize(100,50);
  14. //创建"停止"按钮 按照控件的大小创建窗口
  15. QPushButton * btnStop = new QPushButton("Stop", this);
  16. //移动btnStop按钮
  17. btnStop->move(300,100);
  18. //调整按钮大小
  19. btnStop->resize(100,50);
  20. //重置窗口大小
  21. resize(600,400);
  22. //设置固定窗口大小
  23. setFixedSize(600,400);
  24. //设置窗口标题
  25. setWindowTitle("QThread Test");
  26. WorkThread *pWork = new WorkThread(this);
  27. //Qt4版本的写法
  28. // connect(pWork,SIGNAL(currentNum(int)),this, SLOT(onCurrentNum(int)));
  29. // connect(pWork,SIGNAL(workDone()),this, SLOT(onWorkDone()));
  30. //连接带参数的信号和槽(函数指针的写法)
  31. // void(WorkThread:: *currentNumSigal)(int) = &WorkThread::currentNum;
  32. // void(MainDialog:: *currentNumSlot)(int) = &MainDialog::onCurrentNum;
  33. // connect(pWork, currentNumSigal, this, currentNumSlot);
  34. // void(WorkThread:: *workDoneSigal)(void) = &WorkThread::workDone;
  35. // void(MainDialog:: *workDoneSlot)(void) = &MainDialog::onWorkDone;
  36. // connect(pWork, workDoneSigal, this, workDoneSlot);
  37. //Qt5版本写法(Lambda表达式)
  38. connect(pWork, &WorkThread::currentNum, [=](int val){
  39. this->onCurrentNum(val);
  40. });
  41. connect(pWork, &WorkThread::workDone, [=](){
  42. this->onWorkDone();
  43. });
  44. //关联Start和Stop按钮的信号和槽
  45. connect(btnStart, &QPushButton::clicked, [=](){
  46. qDebug() << "MainThreadId:" << QThread::currentThreadId();
  47. pWork->start();//开启线程
  48. });
  49. connect(btnStop, &QPushButton::clicked, [=](){
  50. pWork->stopThread();
  51. pWork->wait();//结束线程
  52. });
  53. }
  54. MainDialog::~MainDialog()
  55. {
  56. }
  57. void MainDialog::onCurrentNum(int num)
  58. {
  59. qDebug() << "MainDialog::onCurrentNum:" << num;
  60. }
  61. void MainDialog::onWorkDone()
  62. {
  63. qDebug() << "MainDialog::onWorkDone()";
  64. }

点击Start按钮启动线程后,然后通过stop按钮停止线程,在Qt Creator中应用程序输出:

  1. MainThreadId: 0x2638
  2. SubThreadId: 0x3480 i: 0
  3. MainDialog::onCurrentNum: 0
  4. SubThreadId: 0x3480 i: 1
  5. MainDialog::onCurrentNum: 1
  6. SubThreadId: 0x3480 i: 2
  7. MainDialog::onCurrentNum: 2
  8. SubThreadId: 0x3480 i: 3
  9. MainDialog::onCurrentNum: 3
  10. MainDialog::onWorkDone()

可以看到:

0x01、主线程的ThreadId跟子线程的ThreadId不是同一个,证明子线程确实是开了另外一个线程。

0x02、在子线程未执行完毕前,点击关闭程序,会提示:QThread: Destroyed while thread is still running

0x03、但是在子线程未执行完毕之前,点击了stop按钮后,程序停止了计数输出,这时关闭程序,程序不会有上述提示。

需注意的地方:

QThread实例(WorkThread)是属于创建该实例的线程的。比如在主线程中创建一个QThread,那么这个QThread实例本身属于主线程。只有子线程run函数里面定义的变量、实例等是属于新线程的。

发表评论

表情:
评论列表 (有 0 条评论,53人围观)

还没有评论,来说两句吧...

相关阅读

    相关 QThread线编程分析

    QThread多线程编程分析 传统图形界面应用程序都只有一个线程执行,并且一次执行一个操作。如果用户调用一个比较耗时的操作,就会冻结界面响应。一个解决方法是按照事件处理的