• Qt多线程的多种方法之一 QThread


    QThread 似乎是很难的一个东西,特别是信号和槽。以下仅本人学习过程中写的简单案例。

    QThread多线程使用方法

    • QThread类提供了一个与平台无关的管理线程的方法。在Qt中建立线程的主要目的就是为了用线程来处理那些耗时的后台操作,比如大量运算,复制大文件,网络传输等。

    • 使用Qt框架开发应用程序时,使用QThread类可以方便快捷地创建管理多线程。

    • 而多线程之间的通信也可使用Qt特有的“信号-槽”机制实现。

    QThread的使用方法有如下两种:

    1. 继承QThread类
    2. QObject::moveToThread()

    继承QThread方法

    第一种方法很简单,也很好理解,写一个类继承QThread类,并重写run()函数,并在主线程中生成一个ChildThread的实例,并调用对象的start()函数。

    界面(.h):

    #ifndef THREADDLG_H
    #define THREADDLG_H
     
    #include <QDialog>
    #include <QPushButton>
    #include "workthread.h"
    #define MAXSIZE 5							//MAXSIZE宏定义了线程的数目
    class ThreadDlg : public QDialog
    {
        Q_OBJECT
     
    public:
        ThreadDlg(QWidget *parent = 0);
        ~ThreadDlg();
    private:
        QPushButton *startBtn;
        QPushButton *stopBtn;
        QPushButton *quitBtn;
        workThread *myThread[MAXSIZE];		//创建线程指针数组
    public slots:
        void slotStart();						//槽函数用于启动线程
        void slotStop();						//槽函数用于终止线程
    };
     
    #endif // THREADDLG_H
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    界面(.cpp):

    #include "threaddlg.h"
    #include <QHBoxLayout>
    ThreadDlg::ThreadDlg(QWidget *parent)
        : QDialog(parent)
    {
        setWindowTitle(tr("线程"));
        startBtn = new QPushButton(tr("开始"));
        stopBtn = new QPushButton(tr("停止"));
        quitBtn = new QPushButton(tr("退出"));
     
        QHBoxLayout *mainLayout = new QHBoxLayout(this);
        mainLayout->addWidget(startBtn);
        mainLayout->addWidget(stopBtn);
        mainLayout->addWidget(quitBtn);
     
        connect(startBtn,SIGNAL(clicked()),this,SLOT(slotStart()));
        connect(stopBtn,SIGNAL(clicked()),this,SLOT(slotStop()));
        connect(quitBtn,SIGNAL(clicked()),this,SLOT(close()));
    }
     
    void ThreadDlg::slotStart()
    {
        for(int i=0;i<MAXSIZE;i++)
        {
            myThread[i]=new workThread();	//创建五个线程
        }
        for(int i=0;i<MAXSIZE;i++)
        {
            myThread[i]->start();			//启动这五个线程
        }
        startBtn->setEnabled(false);
        stopBtn->setEnabled(true);
    }
     
    void ThreadDlg::slotStop()
    {
        for(int i=0;i<MAXSIZE;i++)
        {
            myThread[i]->terminate();//结束这五个线程
            myThread[i]->wait();//阻塞等待处理结束
        }
        startBtn->setEnabled(true);
        stopBtn->setEnabled(false);
    }
     
    ThreadDlg::~ThreadDlg()
    {
     
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    线程类:(.h)

    #ifndef WORKTHREAD_H
    #define WORKTHREAD_H
     
    #include <QThread>
     
    class workThread:public QThread
    {
    public:
        workThread();
     
    protected:
        void run();
    };
     
    #endif // WORKTHREAD_H
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    若想主线程给子线程传递参数,则可以采用构造函数进行交互数据。

    线程类:(.cpp)

    #include "workthread.h"
    #include <QDebug>
    #include <QVector>
     
    workThread::workThread()
    {
     
    }
     
    void workThread::run()
    {
        QVector<int> nums{1,2,4,3,6,4,2,2,23434,554,232,323,54,66};
        int n = nums.size();
        bool isChange = false;
        while (true) {
            for(int i = 1;i<n;i++){
                for(int j = 0;j<n-i-1;j++){
                    if(nums[j]>nums[j+1])
                    {
                        int t = nums[j];
                        nums[j] = nums[j+1];
                        nums[j+1] = t;
                        isChange = true;
                    }
                }
                if(!isChange){
                    break;
                }
            }
     
            for(int &n:nums){
                qDebug()<<n<<" ";
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    QObject::moveToThread

    说实话这种方法我没有很理解

    1. 定义一个普通的QObject派生类FileWorker,然后将其对象move到QThread中。
    2. 在定义一个转发类也是QObject子类,起名字叫controller,或者叫dummy。将转发类的信号槽和FileWorker类的信号槽关联起来,这样在主线程中调用转发类的槽函数,或者接收信号就OK了。
      大概意思是通过转发类,能使得FileWorker类的槽函数妥当的运行在子线程里面。同时也不需要使用QMutex来进行同步,Qt的事件循环会自己自动处理好这个。

    总结

    推荐做的:
    在QThread子类添加信号。这是绝对安全的,并且也是正确的(发送者的线程依附性没有关系)

    不应该做的是:
    调用moveToThread(this)函数
    指定连接类型:这通常意味着你正在做错误的事情,比如将QThread控制接口与业务逻辑混杂在了一起(而这应该放在该线程的一个独立对象中)
    在QThread子类添加槽函数:这意味着它们将在错误的线程被调用,也就是QThread对象所在线程,而不是QThread对象管理的线程。这又需要你指定连接类型或者调用moveToThread(this)函数
    使用QThread::terminate()函数

    不能做的是:
    在线程还在运行时退出程序。使用QThread::wait()函数等待线程结束
    在QThread对象所管理的线程仍在运行时就销毁该对象。如果你需要某种“自行销毁”的操作,你可以把finished()信号同deleteLater()槽连接起来

    参考:https://blog.csdn.net/zb872676223/article/details/22718087

  • 相关阅读:
    细说MCU输出两路PWM波形及改变占空比的实现方法
    制作一个ros2机器人需要学习的课本(还不全面)
    【图形基础篇】01 # 浏览器中实现可视化的四种方式
    分享一个基于微信小程序的医院口腔助手小程序 牙科诊所预约小程序 源码 lw 调试
    代码随想录算法训练营第四十六天|139. 单词拆分、多重背包问题、总结
    【计网】第六章 应用层
    transformer7
    kubekey 离线部署 kubesphere v3.3.0
    Apache Flink Table Store 0.2.0 发布
    SpringCloud-基于SpringAMQP实现消息队列
  • 原文地址:https://blog.csdn.net/qq_43142509/article/details/125600002