• Qt:线程


    Qt线程简介

    Qt提供了QThread类表示线程

    • 创建线程的步骤:
    • 创建QThread类的实例
    • 指定线程的入口函数
      对于Linux的线程函数,是在参数部分使用函数指针指定线程的入口函数
      对于Qt,则先创建QThread子类,再重写其中的run函数,起到指定入口函数的方式(这里多态)同事件处理

    常用函数

    start,调用系统API创建线程,新线程创建出来后会自动执行run函数
    finish,线程结束时会发出信号,可以通过该信号来实现线程的清理工作
    在Qt中,为了防止多线程修改界面的不安全现象,Qt选择一刀切,对于界面的控件状态修改必须在主线程中进行

    分为三个部分,公有函数、公有槽函数,信号,Qt提供的静态成员函数
    父对象和父类是两回事,父类是继承关系,父对象是从属关系

    // 构造函数
    QThread::QThread(QObject *parent = Q_NULLPTR);
    指定一个父对象就可以了
    
    // 判断线程中的任务是不是处理完毕了
    bool QThread::isFinished() const;
    // 判断子线程是不是在执行任务
    bool QThread::isRunning() const;
    
    // Qt中的线程可以设置优先级
    // 得到当前线程的优先级
    Priority QThread::priority() const;
    void QThread::setPriority(Priority priority);
    优先级:
        QThread::IdlePriority         --> 最低的优先级
        QThread::LowestPriority
        QThread::LowPriority
        QThread::NormalPriority
        QThread::HighPriority
        QThread::HighestPriority
        QThread::TimeCriticalPriority --> 最高的优先级
        
        QThread::InheritPriority      --> 子线程和其父线程的优先级相同, 默认是这个
    
    // 退出线程, 停止底层的事件循环
    // 退出线程的工作函数
    void QThread::exit(int returnCode = 0);
    // 调用线程退出函数之后, 线程不会马上退出因为当前任务有可能还没有完成, 调回用这个函数是
    // 调用wait后会等待任务执行, 执行完退出线程, 一般情况下会在 exit() 后边调用这个函数
    bool QThread::wait(unsigned long time = ULONG_MAX);
    
    

    // 和调用 exit() 效果是一样的
    // 调用这个函数之后, 再调用 wait() 函数
    [slot] void QThread::quit();
    
    // 启动子线程
    [slot] void QThread::start(Priority priority = InheritPriority);
    
    // 线程退出, 可能是会马上终止线程, 一般情况下不使用这个函数
    [slot] void QThread::terminate();
    

    信号

    // 线程中完成后, 会发出该信号
    [signal] void QThread::finished();
    
    // 发出该信号后表示线程开始工作,启动线程后没必要捕捉该信号
    [signal] void QThread::started();
    

    主线程和子线程间通过信号和槽进行数据传递

    静态函数

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

    任务处理函数

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

    该函数没有参数和返回值,是一个受保护的虚函数,如果让子线程处理某些任务,需要将任务写到run内部,调用start函数就能执行线程内部的run函数

    受保护说明该函数不能在类外使用,需要通过调用start槽函数使run函数运行执行任务

    最简单的Qt线程

    ------------------------------------------------
    Widget.h
    
    #include 
    #include "thread.h"
    QT_BEGIN_NAMESPACE
    namespace Ui { class Widget; }
    QT_END_NAMESPACE
    
    class Widget : public QWidget
    {
        Q_OBJECT
    
    public:
        Widget(QWidget *parent = nullptr);
        ~Widget();
    
    private:
        Ui::Widget *ui;
    
        Thread thread;
        void handle();
    };
    
    ------------------------------------------------------------
    Widget,cpp
    
    #include "widget.h"
    #include "ui_widget.h"
    
    Widget::Widget(QWidget *parent)
        : QWidget(parent)
        , ui(new Ui::Widget)
    {
        ui->setupUi(this);
    
        //在构造函数中启动线程
        thread.start();
    
        //连接信号槽,通过槽函数更新界面
        connect(&thread,&Thread::notify,this,&Widget::handle);
    }
    
    Widget::~Widget()
    {
        delete ui;
    }
    
    void Widget::handle()
    {
        //修改界面内容
        int val=ui->lcdNumber->intValue();
        val--;
        ui->lcdNumber->display(val);
    
    }
    
    --------------------------------------
    Thread.h    继承自QThread
    #include 
    #include 
    class Thread : public QThread
    {
        Q_OBJECT
    public:
        Thread();
    
        //最重要的目的是重写run函数
        void run();
    
    signals:
        void notify();
    };
    
    -------------------------------------------
    #include "thread.h"
    
    Thread::Thread()
    {
    
    }
    
    void Thread::run()
    {
        //在run函数中不能修改界面的内容
        //可以在run中计时,每过一秒通知主线程去修改界面计时器的值
        for(int i=0;i<10;i++)
        {
            sleep(1);
            //休眠完后,发送信号通知主线程
            emit notify();
        }
    }
    

    使用方式

    方式1:

    • 创建线程类,该类继承QThread类
    • 在线程的子类中重写run函数
    • 在主线程类中new一个子线程的类对象,让该子线程对象调用start函数

    子类头文件

    #include 
    class MyThread : public QThread
    {
        Q_OBJECT
    public:
        explicit MyThread(QObject *parent = nullptr);
    
    protected:
        void run();
    
    signals:
        // 自定义信号, 传递数据
        void curNumber(int num);
    
    public slots:
    };
    

    子类源文件

    #include "mythread.h"
    #include 
    MyThread::MyThread(QObject *parent) : QThread(parent)
    {}
    
    void MyThread::run()
    {
        qDebug() << "当前线程对象的地址: " << QThread::currentThread();
    
        int num = 0;
        while(1)
        {
            emit curNumber(num++);
            if(num == 10000000)
            {
                break;
            }
            QThread::usleep(1);
        }
        qDebug() << "run() 执行完毕, 子线程退出...";
    }
    
    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    #include "mythread.h"
    #include 
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    
        qDebug() << "主线程对象地址:  " << QThread::currentThread();
        
        // 创建子线程
        MyThread* subThread = new MyThread;
        connect(subThread, &MyThread::curNumber, this, [=](int num)
        {
            ui->label->setNum(num);
        });
    
        connect(ui->startBtn, &QPushButton::clicked, this, [=]()
        {
            // 启动子线程
            subThread->start();
        });
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
  • 相关阅读:
    【Book And Paper 】
    【LeetCode动态规划#13】买卖股票含冷冻期(状态众多,比较繁琐)、含手续费
    Mysql存储-EAV模式
    技术分享 | app自动化测试(Android)-- 特殊控件 Toast 识别
    JAVASE第十五天
    关于kafka-python的若干问题
    Docker部署PgSQL&Adminer
    css h5 端弹窗时禁止底部页面滚动
    Andriod 获取手机CPU型号设备信息
    什么是Nacos及实战使用教程
  • 原文地址:https://blog.csdn.net/qq_45481606/article/details/128241933