QT多线程的实现方式:QThread run优雅的创建与退出【QT多线程】

亦凉 2024-03-04 08:05 72阅读 0赞

qt通过继承实现线程的方法有两种

  1. 继承QThread,然后重写run函数实现多线程
  2. 继承QObject,使用moveToThread函数实现多线程

本文介绍第一种的创建、使用与退出。

一、QThread类的run介绍

1、QThread::run函数的使用

QThread 是用来管理线程的,它所依附的线程和它管理的新线程并不是同一个东西QThread 所依附的线程,就是执行创建QThread的线程。也就是咱们这儿的主线程,QThread 管理的新线程,就是 run 启动的线程。

所以总结一句话:QThread只有run函数是在新线程里的,其他所有函数都在QThread生成的线程里

那么就抛出两个问题

1.QThread的对象依附在主线程中,次线程的slot函数会在主线程中执行,而不是次线程。除非:(不建议这样做)

  • QThread 对象通过movetoThread依附到次线程中(这里涉及到实现多线程的第二种方式)
  • slot 和信号是直接连接,且信号在次线程中发射(这里涉及到QTconnect的第五个参数)

2.QThread的继承类的其他函数尽量别要有太耗时的操作,要确保所有耗时的操作都在run函数里

2、QThread run方式特点

2.1优点:可以通过信号槽与外界进行通信。
2.2缺点:

每次新建一个线程都需要继承QThread,实现一个新类,使用不太方便。
要自己进行资源管理,线程释放和删除。并且频繁的创建和释放会带来比较大的内存开销。

2.3适用场景:QThread适用于那些常驻内存的任务。

二、QThread run实现一个简单线程

新建一个集成QThread的类,重写虚函数run,通过run启动线程

代码:https://download.csdn.net/download/qq_43445867/88332781

1、基本流程

  1. #ifndef QTHREAD_RUN_H
  2. #define QTHREAD_RUN_H
  3. class QThread_Run
  4. {
  5. public:
  6. QThread_Run();
  7. };
  8. #endif // QTHREAD_RUN_H

1.1需要创建一个线程类的子类,让其继承 QT 中的线程类 QThread,比如:

  1. #ifndef QTHREAD_RUN_H
  2. #define QTHREAD_RUN_H
  3. #include <QThread>
  4. class QThread_Run : public QThread
  5. {
  6. Q_OBJECT
  7. public:
  8. explicit QThread_Run(QObject *parent = nullptr);
  9. QThread_Run();
  10. };
  11. #endif // QTHREAD_RUN_H

1.2 重写父类的 run () 方法,在该函数内部编写子线程要处理的具体的业务流程

  1. #ifndef QTHREAD_RUN_H
  2. #define QTHREAD_RUN_H
  3. #include <QThread>
  4. class QThread_Run : public QThread
  5. {
  6. Q_OBJECT
  7. public:
  8. explicit QThread_Run(QObject *parent = nullptr);
  9. QThread_Run();
  10. protected:
  11. void run();
  12. };
  13. #endif // QTHREAD_RUN_H

1.3 在主线程中创建子线程对象,new 一个就可以了

  1. QThread_Run* qthread_run=new QThread_Run(this);

1.4 启动子线程,调用 start () 方法

  1. qthread_run->start();

不能在类的外部调用 run () 方法启动子线程,在外部调用 start () 相当于让 run () 开始运行

2、完整代码

qthread_run.h

  1. #ifndef QTHREAD_RUN_H
  2. #define QTHREAD_RUN_H
  3. #include <QThread>
  4. class QThread_Run : public QThread
  5. {
  6. Q_OBJECT
  7. public:
  8. explicit QThread_Run(QObject *parent = nullptr);
  9. QThread_Run();
  10. protected:
  11. void run();
  12. signals:
  13. void result(int);
  14. };
  15. #endif // QTHREAD_RUN_H

qthread_run.c

  1. #include "qthread_run.h"
  2. #include <QEventLoop>
  3. #include <QTimer>
  4. QThread_Run::QThread_Run(QObject *parent): QThread(parent)
  5. {
  6. }
  7. void Delay_MSec(unsigned int msec)
  8. {
  9. QEventLoop loop;//定义一个新的事件循环
  10. QTimer::singleShot(msec, &loop, SLOT(quit())); //创建单次定时器,槽函数为事件循环的退出函数
  11. loop.exec(); //事件循环开始执行,程序会卡在这里,直到定时时间到,本循环被退出
  12. }
  13. void QThread_Run::QThread_Run::run()
  14. {
  15. for(int i=0;i<10000;i++)
  16. {
  17. emit result(i);
  18. Delay_MSec(100);
  19. }
  20. }

mainwindow.c

  1. #include "mainwindow.h"
  2. #include "ui_mainwindow.h"
  3. MainWindow::MainWindow(QWidget *parent)
  4. : QMainWindow(parent)
  5. , ui(new Ui::MainWindow)
  6. {
  7. ui->setupUi(this);
  8. QThread_Run* qthread_run=new QThread_Run(this);
  9. connect(qthread_run,&QThread_Run::result,this,[=](int num)
  10. {
  11. ui->lcdNumber->display(num);
  12. });
  13. connect(ui->pushButton,&QPushButton::clicked, this, [=]()
  14. {
  15. qthread_run->start();
  16. });
  17. }
  18. MainWindow::~MainWindow()
  19. {
  20. delete ui;
  21. }

mainwindow.h

  1. #ifndef MAINWINDOW_H
  2. #define MAINWINDOW_H
  3. #include <QMainWindow>
  4. #include "qthread_run.h"
  5. QT_BEGIN_NAMESPACE
  6. namespace Ui { class MainWindow; }
  7. QT_END_NAMESPACE
  8. class MainWindow : public QMainWindow
  9. {
  10. Q_OBJECT
  11. public:
  12. MainWindow(QWidget *parent = nullptr);
  13. ~MainWindow();
  14. private:
  15. Ui::MainWindow *ui;
  16. };
  17. #endif // MAINWINDOW_H

三、优雅的停止创建的线程

quitexit函数都不会中途终端线程,要马上终止一个线程可以使用terminate函数,但这个函数存在非常不安定因素,会提示以下错误

  1. QObject::killTimer: Timers cannot be stopped from another thread

停止创建的线程分两种情况

1、不使用事件循环

run函数内有一个 while 或 for 的死循环

最简单的方法是添加一个bool变量,设置一个标记来控制死循环的退出,通过主线程修改这个bool变量来进行终止,但这样有可能引起访问冲突,需要加锁

完整代码:https://download.csdn.net/download/qq_43445867/88332782

需要在原来的头文件加上如下语句

qthread_run.h

  1. #ifndef QTHREAD_RUN_H
  2. #define QTHREAD_RUN_H
  3. #include <QThread>
  4. class QThread_Run : public QThread
  5. {
  6. Q_OBJECT
  7. public:
  8. explicit QThread_Run(QObject *parent = nullptr);
  9. QThread_Run();
  10. bool m_isCanRun;//加入标志位
  11. //加入槽函数
  12. public slots:
  13. void stopImmediately();
  14. protected:
  15. void run();
  16. signals:
  17. void result(int);
  18. };
  19. #endif // QTHREAD_RUN_H

修改源文件以及run函数

qthread_run.c

  1. #include "qthread_run.h"
  2. #include <QEventLoop>
  3. #include <QTimer>
  4. QThread_Run::QThread_Run(QObject *parent): QThread(parent)
  5. {
  6. }
  7. void Delay_MSec(unsigned int msec)
  8. {
  9. QEventLoop loop;//定义一个新的事件循环
  10. QTimer::singleShot(msec, &loop, SLOT(quit())); //创建单次定时器,槽函数为事件循环的退出函数
  11. loop.exec(); //事件循环开始执行,程序会卡在这里,直到定时时间到,本循环被退出
  12. }
  13. //增加槽函数处理
  14. void QThread_Run::stopImmediately()
  15. {
  16. QMutexLocker locker(&m_lock);
  17. m_isCanRun = false;
  18. }
  19. void QThread_Run::QThread_Run::run()
  20. {
  21. for(int i=0;i<10000;i++)
  22. {
  23. //增加判断
  24. QMutexLocker locker(&m_lock);
  25. if(!m_isCanRun)//在每次循环判断是否可以运行,如果不行就退出循环
  26. {
  27. return;
  28. }
  29. emit result(i);
  30. Delay_MSec(100);
  31. }
  32. }

发表评论

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

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

相关阅读

    相关 QtQt线技术

    Qt提供了许多用于处理线程的类和函数。 Qt程序员可以使用以下四种不同的方法来实现多线程应用程序。 QThread:具有可选事件循环的低级API `QThread`是Q

    相关 Qt线

    今天学习Qt的多线程,在学习多线程主要是两个方面。一是多线程的基础概念,二是多线程的同步,三是怎么和主线程进行通讯。 三.Qt 的应用程序开始执行的时候,只有一个主线程在运行

    相关 QT线

    QT多线程,QT4.7之前是承继 QThread 的方式,比较简单,而 QT5之后,使用了信号和槽的方式,比较灵活。(版本信息可能有误) 下面是对QT线程信息的注意事项