参考视频:https://www.bilibili.com/video/BV1XW411x7NU?p=74

使用多线程的好处:提高应用程序响应速度、使多CPU更加高效、改善程序结构。

在Qt中使用QThread来管理线程。Qt中使用线程时,需要自己实现一个thread的类。

(1)基本使用

功能说明如下:

 

 

 工程文件有:

 

 

 mythread.h和mythread.cpp是自定义的线程类,需要改为继承自QThread,QThread类有一个虚函数run(),它就是线程处理函数,我们需要重写它。当我们调用QThread的start()函数时,会间接的调用run()函数。

widget.h和widget.cpp是主窗口的代码。

mythread.h的代码:

  1. 1 #ifndef MYTHREAD_H
  2. 2 #define MYTHREAD_H
  3. 3
  4. 4 #include <QObject>
  5. 5 #include <QThread>
  6. 6
  7. 7 class MyThread : public QThread
  8. 8 {
  9. 9 Q_OBJECT
  10. 10 public:
  11. 11 explicit MyThread(QObject *parent = nullptr);
  12. 12
  13. 13 signals:
  14. 14 void isDone();
  15. 15
  16. 16 protected:
  17. 17 //QThread的虚函数,线程处理函数
  18. 18 //不能直接调用,通过start()间接调用
  19. 19 void run();
  20. 20
  21. 21 public slots:
  22. 22 };
  23. 23
  24. 24 #endif // MYTHREAD_H

View Code

mythread.cpp代码:

  1. 1 #include "mythread.h"
  2. 2
  3. 3 MyThread::MyThread(QObject *parent) : QThread(parent)
  4. 4 {
  5. 5
  6. 6 }
  7. 7
  8. 8 void MyThread::run()
  9. 9 {
  10. 10 //很复杂的数据处理,需要耗时5s
  11. 11 sleep(5);
  12. 12 //发送处理完成信号
  13. 13 emit isDone();
  14. 14 }

View Code

widget.h代码:

  1. 1 #ifndef WIDGET_H
  2. 2 #define WIDGET_H
  3. 3
  4. 4 #include <QWidget>
  5. 5 #include <QTimer> //定时器
  6. 6 #include "mythread.h" //线程
  7. 7
  8. 8 namespace Ui {
  9. 9 class Widget;
  10. 10 }
  11. 11
  12. 12 class Widget : public QWidget
  13. 13 {
  14. 14 Q_OBJECT
  15. 15
  16. 16 public:
  17. 17 explicit Widget(QWidget *parent = 0);
  18. 18 ~Widget();
  19. 19
  20. 20 void dealTimeout(); //定时器处理函数
  21. 21 void dealThread(); //处理子线程发来的信号
  22. 22 void stopThread(); //停止线程
  23. 23
  24. 24 private slots:
  25. 25 void on_pushButton_start_clicked();
  26. 26
  27. 27 private:
  28. 28 Ui::Widget *ui;
  29. 29
  30. 30 QTimer *timer = NULL;
  31. 31 MyThread *thread = NULL;
  32. 32 };
  33. 33
  34. 34 #endif // WIDGET_H

View Code

widget.cpp代码:

  1. 1 #include "widget.h"
  2. 2 #include "ui_widget.h"
  3. 3 #include <QThread>
  4. 4 #include <QDebug>
  5. 5
  6. 6 Widget::Widget(QWidget *parent) :
  7. 7 QWidget(parent),
  8. 8 ui(new Ui::Widget)
  9. 9 {
  10. 10 ui->setupUi(this);
  11. 11
  12. 12 timer = new QTimer(this);
  13. 13 //分配空间
  14. 14 thread = new MyThread(this);
  15. 15
  16. 16 //只要定时器启动,自动触发timerout()信号
  17. 17 connect(timer, &QTimer::timeout, this, &Widget::dealTimeout);
  18. 18 //接收子线程发送的isDone信号并处理
  19. 19 connect(thread, &MyThread::isDone, this, &Widget::dealThread);
  20. 20 //当按窗口右上角x时(关闭窗口),触发
  21. 21 connect(this, &Widget::destroyed, this, &Widget::stopThread);
  22. 22 }
  23. 23
  24. 24 Widget::~Widget()
  25. 25 {
  26. 26 delete ui;
  27. 27 }
  28. 28
  29. 29 void Widget::dealTimeout()
  30. 30 {
  31. 31 static int i = 0;
  32. 32 i++;
  33. 33 //设定lcd的值
  34. 34 ui->lcdNumber->display(i);
  35. 35 }
  36. 36
  37. 37 void Widget::dealThread()
  38. 38 {
  39. 39 //处理完数据后,关闭定时器
  40. 40 timer->stop();
  41. 41 qDebug() << "timer turn off!!!";
  42. 42 }
  43. 43
  44. 44 void Widget::stopThread()
  45. 45 {
  46. 46 //停止线程
  47. 47 thread->quit();
  48. 48 //等待线程处理完事情
  49. 49 thread->wait();
  50. 50 }
  51. 51
  52. 52 void Widget::on_pushButton_start_clicked()
  53. 53 {
  54. 54 if (timer->isActive() == false) {
  55. 55 timer->start(100);
  56. 56 }
  57. 57 //启动线程,处理数据
  58. 58 thread->start();
  59. 59 }

View Code

运行测试:

可以看到计数了45次之后(每次100ms),定时器停止了,表示接收到了线程发送的信号(线程睡眠5s之后发出的)。可能会有疑问为什么不是50次(刚好5s),那是因为我们先启动定时器,在去启动线程,这个过程需要花费时间。

(2)多线程的使用

多线程的实现模型如下,记下来就好了:

 

 

 先给出实现的代码,后面再介绍。

工程文件有:

 

 

 mythread.h和mythread.cpp是自定义的线程类,继承自QThread,和前一个例子不一样。

widget.h和widget.cpp是主窗口的代码。

mythread.h代码:

  1. 1 #ifndef MYTHREAD_H
  2. 2 #define MYTHREAD_H
  3. 3
  4. 4 #include <QObject>
  5. 5
  6. 6 class MyThread : public QObject
  7. 7 {
  8. 8 Q_OBJECT
  9. 9 public:
  10. 10 explicit MyThread(QObject *parent = nullptr);
  11. 11
  12. 12 //线程处理函数
  13. 13 void myTimerout();
  14. 14 //设置flag,用于判断是否结束线程处理函数的while循环
  15. 15 void setFlag(bool flag = true);
  16. 16
  17. 17 signals:
  18. 18 void mySignal();
  19. 19
  20. 20 public slots:
  21. 21
  22. 22 private:
  23. 23 bool isStop;
  24. 24 };
  25. 25
  26. 26 #endif // MYTHREAD_H

View Code

mythread.c代码:

  1. 1 #include "mythread.h"
  2. 2 #include <QThread>
  3. 3 #include <QDebug>
  4. 4
  5. 5 MyThread::MyThread(QObject *parent) : QObject(parent)
  6. 6 {
  7. 7 isStop = false;
  8. 8 }
  9. 9
  10. 10 void MyThread::myTimerout()
  11. 11 {
  12. 12 while (isStop == false) {
  13. 13 QThread::sleep(1);
  14. 14 emit mySignal();
  15. 15 qDebug() << "子线程号:" << QThread::currentThread();
  16. 16 if (true == isStop) {
  17. 17 break;
  18. 18 }
  19. 19 }
  20. 20 }
  21. 21
  22. 22 void MyThread::setFlag(bool flag)
  23. 23 {
  24. 24 isStop = flag;
  25. 25 }

View Code

widget.h代码:

  1. 1 #ifndef WIDGET_H
  2. 2 #define WIDGET_H
  3. 3
  4. 4 #include <QWidget>
  5. 5 #include "mythread.h"
  6. 6 #include <QThread>
  7. 7
  8. 8 namespace Ui {
  9. 9 class Widget;
  10. 10 }
  11. 11
  12. 12 class Widget : public QWidget
  13. 13 {
  14. 14 Q_OBJECT
  15. 15
  16. 16 public:
  17. 17 explicit Widget(QWidget *parent = 0);
  18. 18 ~Widget();
  19. 19
  20. 20 void dealsignal();
  21. 21 void dealclose();
  22. 22 signals:
  23. 23 //启动子线程的信号
  24. 24 void startThreadSignal();
  25. 25
  26. 26 private slots:
  27. 27 void on_pushButton_start_clicked();
  28. 28
  29. 29 void on_pushButton_stop_clicked();
  30. 30
  31. 31 private:
  32. 32 Ui::Widget *ui;
  33. 33 MyThread *mythread = NULL;
  34. 34 QThread *thread = NULL;
  35. 35 };
  36. 36
  37. 37 #endif // WIDGET_H

View Code

widget.cpp代码:

  1. 1 #include "widget.h"
  2. 2 #include "ui_widget.h"
  3. 3 #include <QDebug>
  4. 4
  5. 5 Widget::Widget(QWidget *parent) :
  6. 6 QWidget(parent),
  7. 7 ui(new Ui::Widget)
  8. 8 {
  9. 9 ui->setupUi(this);
  10. 10 //动态分配空间,不能指定父对象
  11. 11 mythread = new MyThread;
  12. 12 //创建子线程
  13. 13 thread = new QThread(this);
  14. 14 //把自定义的线程加入到子线程中
  15. 15 mythread->moveToThread(thread);
  16. 16
  17. 17 //处理子线程发送的信号
  18. 18 connect(mythread, &MyThread::mySignal, this, &Widget::dealsignal);
  19. 19 qDebug() << "主线程号:" << QThread::currentThread();
  20. 20 //发送信号给子线程,通过信号和槽调用子线程的线程处理函数
  21. 21 connect(this, &Widget::startThreadSignal, mythread, &MyThread::myTimerout);
  22. 22 //关闭主窗口
  23. 23 connect(this, &Widget::destroyed, this, &Widget::dealclose);
  24. 24 }
  25. 25
  26. 26 Widget::~Widget()
  27. 27 {
  28. 28 delete ui;
  29. 29 }
  30. 30
  31. 31 void Widget::dealsignal()
  32. 32 {
  33. 33 static int i = 0;
  34. 34 i++;
  35. 35 ui->lcdNumber->display(i);
  36. 36 }
  37. 37
  38. 38 void Widget::dealclose()
  39. 39 {
  40. 40 mythread->setFlag(true);
  41. 41 thread->quit();
  42. 42 thread->wait();
  43. 43 delete mythread;
  44. 44 }
  45. 45
  46. 46 void Widget::on_pushButton_start_clicked()
  47. 47 {
  48. 48 if (thread->isRunning() == true) {
  49. 49 return;
  50. 50 }
  51. 51 //启动线程,但是没有启动线程处理函数
  52. 52 thread->start();
  53. 53 mythread->setFlag(false);
  54. 54 //不能直接调用线程处理函数
  55. 55 //直接调用导致线程处理函数和主线程在同一个线程
  56. 56 //只能通过信号和槽调用
  57. 57 emit startThreadSignal();
  58. 58 }
  59. 59
  60. 60 void Widget::on_pushButton_stop_clicked()
  61. 61 {
  62. 62 if (thread->isRunning() == false) {
  63. 63 return;
  64. 64 }
  65. 65 mythread->setFlag(true);
  66. 66 thread->quit();
  67. 67 thread->wait();
  68. 68 }

View Code

需要说明以下几点:

(1)创建自定义线程变量时,不能指定父对象mythread=newMyThread;),因为调用moveToThread函数之后,变量在创建的线程中使用回收,而不是在主线程。

(2)子线程会睡眠1s就发送一个信号mySignal给主线程,主线程接收信号,在槽函数中将数值累加一,并在LCD上显示。

(3)主线程通过信号和槽的方式调用子线程处理函数,主线程发送信号给子线程,槽函数就是子线程的线程处理函数。

(4)子线程setFlag()的作用是:关闭子线程时,quit()函数会等待子线程执行结束之后再回收,但是如果不设置标志,while循环会一直执行,子线程也就没有结束。

运行测试:

注意看打印的信息,可以看到子线程和主线程的id。

 

版权声明:本文为mrlayfolk原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/mrlayfolk/p/13303035.html