• Qt多线程及线程池的使用笔记


    在进行桌面应用程序开发的时候,假设应用程序在某些情况下血要处理较为复杂的逻辑,如果只有一个线程去处理,就会导致窗口卡顿,无法处理用户的相关操作,这种情况下就学要使用多线程,其中一个线程处理窗口事件,其他线程处理运算逻辑,多个线程各司其职,不仅可以提高用户体验还可以提升程序的执行效率。

    在Qt中使用了多线程,需要注意的一些概念是:

    • 默认的线程在Qt中称之为窗口线程,也就叫主线程,负责窗口事件处理或者窗口控件数据的更新;
    • 子线程负责后台的业务逻辑处理,子线程中不能对窗口对象做任何操作,这些事情需要交给窗口线程处理;
    • 主线程和子线程之间如果要进行数据的传递,需要使用Qt中的信号槽机制
    • 子线程一般不允许越级进行对窗口参数进行操作

    (一)Qt线程类QThread简介

    ​ Qt提供了一个线程类QThread ,通过这个类就需要可以创建子线程了,Qt中一共提供了两种创建子线程的方式,后边会依次介绍其他使用方式,在本例中提供QThread中详细使用方法基本案例。

    本文福利, 免费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块等等)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

    1.头文件

    #include
    

    2.QThread 其常用API介绍

    1. // QThread 类常用 API
    2. // 构造函数
    3. QThread::QThread(QObject *parent = Q_NULLPTR);
    4. // 判断线程中的任务是不是处理完毕了
    5. bool QThread::isFinished() const;
    6. // 判断子线程是不是在执行任务
    7. bool QThread::isRunning() const;
    8. // Qt中的线程可以设置优先级
    9. // 得到当前线程的优先级
    10. Priority QThread::priority() const;
    11. void QThread::setPriority(Priority priority);
    12. 优先级:
    13. QThread::IdlePriority --> 最低的优先级
    14. QThread::LowestPriority
    15. QThread::LowPriority
    16. QThread::NormalPriority
    17. QThread::HighPriority
    18. QThread::HighestPriority
    19. QThread::TimeCriticalPriority --> 最高的优先级
    20. QThread::InheritPriority --> 子线程和其父线程的优先级相同, 默认是这个
    21. // 退出线程, 停止底层的事件循环
    22. // 退出线程的工作函数
    23. void QThread::exit(int returnCode = 0);
    24. // 调用线程退出函数之后, 线程不会马上退出因为当前任务有可能还没有完成, 调回用这个函数是
    25. // 等待任务完成, 然后退出线程, 一般情况下会在 exit() 后边调用这个函数
    26. bool QThread::wait(unsigned long time = ULONG_MAX);

    3.QThread的信号与槽函数

    1. // 和调用 exit() 效果是一样的
    2. // 代用这个函数之后, 再调用 wait() 函数
    3. [slot] void QThread::quit();
    4. // 启动子线程
    5. [slot] void QThread::start(Priority priority = InheritPriority);
    6. //线程退出, 可能是会马上终止线程, 一般情况下不使用这个函数
    7. [slot] void QThread::terminate();
    8. // 线程中执行的任务完成了, 发出该信号
    9. // 任务函数中的处理逻辑执行完毕了
    10. [signal] void QThread::finished();
    11. // 开始工作之前发出这个信号, 一般不使用
    12. [signal] void QThread::started();

    4.QThread静态函数 

    1. // 返回一个指向管理当前执行线程的QThread的指针
    2. [static] QThread *QThread::currentThread();
    3. // 返回可以在系统上运行的理想线程数 == 和当前电脑的 CPU 核心数相同
    4. [static] int QThread::idealThreadCount();
    5. // 线程休眠函数
    6. [static] void QThread::msleep(unsigned long msecs); // 单位: 毫秒
    7. [static] void QThread::sleep(unsigned long secs); // 单位: 秒
    8. [static] void QThread::usleep(unsigned long usecs); // 单位: 微秒

    5.任务处理函数

    1. // 子线程要处理什么任务, 需要写到 run() 中
    2. [virtual protected] void QThread::run();

    这个 run() 是一个虚函数,如果想让创建的子线程执行某个任务,需要写一个子类让其继承 QThread,并且在子类中重写父类的 run() 方法,函数体就是对应的任务处理流程。另外,这个函数是一个受保护的成员函数,不能够在类的外部调用,如果想要让线程执行这个函数中的业务流程,需要通过当前线程对象调用槽函数 start() 启动子线程,当子线程被启动,这个 run() 函数也就在线程内部被调用了

    (二)两种基本QThread类使用方法

    ​ 关于QThread是Qt中用于在多线程中运行代码的核心类,该类是QObject的一个子类。

    ​ 关于QThread如何使用,Qt官方提供了两种方法:

    ​ 1.方法一

    ​ 步骤一:子类化QThread(定义一个类继承QThread)并重新实现run();

    ​ 步骤二:然后创建子类的实例并调用start()成员函数运行线程。

    ​ 具体操作如下代码片段:

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

    1. class MyThread:public QThread
    2. {
    3. ......
    4. }

    ​ Step Two:重写父类的run()方法,在该函数内部编写具体的业务流程:

    1. class MyThread:public QThread
    2. {
    3. ......
    4. protected:
    5. void run()
    6. {
    7. ........
    8. }
    9. }

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

    MyThread * subThread = new MyThread;
    

    Step Four : 启动子线程,调用start()方法:

    subThread->start();
    

    注意:当子线程别创建出来之后,父子线程之间的通信可以通过信号槽的方式:

    1. 在 Qt 中在子线程中不要操作程序中的窗口类型对象,不允许,如果操作了程序就挂了;
    2. 只有主线程才能操作程序中的窗口对象,默认的线程就是主线程,自己创建的就是子线程。

    2.方法二

    ​ Qt 提供的第二种线程的创建方式弥补了第一种方式的缺点,用起来更加灵活,但是这种方式写起来会相对复杂一些,其具体操作步骤如下:

    ​ Step One:创建一个新的类,让这个类从QObejct派生:

    1. class MyWork:public QObject
    2. {
    3. .......
    4. }

    Step Two:在这个类中添加一个公共的成员函数,函数体就是我们要子线程中执行的业务逻辑:

    1. class MyWork:public QObject
    2. {
    3. public:
    4. .......
    5. // 函数名自己指定, 叫什么都可以, 参数可以根据实际需求添加
    6. void working();
    7. }

    Step Three:在主线程中创建一个 QThread 对象,这就是子线程的对象,new一个就可以了

    QThread* sub = new QThread;
    

    Step Four:在主线程中创建工作的类对象(千万不要指定给创建的对象指定父对象)

    1. MyWork* work = new MyWork(this); // error
    2. MyWork* work = new MyWork; // ok

    Step Five:将 MyWork 对象移动到创建的子线程对象中,需要调用 QObject 类提供的 moveToThread() 方法

    1. // void QObject::moveToThread(QThread *targetThread);
    2. // 如果给work指定了父对象, 这个函数调用就失败了
    3. // 提示: QObject::moveToThread: Cannot move objects with a parent
    4. work->moveToThread(sub); // 移动到子线程中工作

    Step Sixth: 启动子线程,调用 start(), 这时候线程启动了,但是移动到线程中的对象并没有工作

    Step Seventh: 调用 MyWork 类对象的工作函数,让这个函数开始执行,这时候是在移动到的那个子线程中运行的

    注意:

       使用这种多线程方式,假设有多个不相关的业务流程需要被处理,那么就可以创建多个类似于 MyWork 的类,将业务流程放多类的公共成员函数中,然后将这个业务类的实例对象移动到对应的子线程中 moveToThread() 就可以了,这样可以让编写的程序更加灵活,可读性更强,更易于维护。

    本文福利, 免费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块等等)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

  • 相关阅读:
    Linux--安装部署Docker
    实战-COVID-19-KSH(html+ python +django +爬虫 +pyecharts 实时疫情动态)内附MySQL详细安装配置教程
    ADAU1860调试心得(2)硬件和软件的详细说明
    自定义注解以及注解在反射中的应用
    【Unity开发小技巧】Unity打包IOS端APP
    koa使用Sequelize:定义数据结构
    管理 Python 依赖项
    SIGIR‘22 推荐系统论文之对比学习篇
    面试经典150题——Day21
    python的opencv操作记录(五) - 插值第一篇
  • 原文地址:https://blog.csdn.net/m0_60259116/article/details/127753851