当前位置: 首页 > news >正文

[Qt][Qt 多线程][上]详细讲解

目录

  • 0.Qt 多线程概述
  • 1.如何看待客户端的多线程?
  • 2.Qt 线程的使用条件
  • 3.创建线程的方法
    • 1.方法一
    • 2.方法二
    • 3.说明
    • 4.关闭线程使用的方法


0.Qt 多线程概述

  • Qt中,多线程的处理⼀般是通过QThread类来实现
  • QThread代表⼀个在应⽤程序中可以独⽴控制的线程,也可以和进程中的其他线程共享数据
  • QThread对象管理程序中的⼀个控制线程,QThreadrun()中开始执⾏
    • 默认情况下,run()通过调⽤exec()来启动事件循环,并在线程内运⾏Qt事件循环

1.如何看待客户端的多线程?

  • 服务器利用多线程,最主要的目的是充分利用多核CPU的计算资源
  • 客户端对于普通用户,"使用体验"很重要
    • 客户端上的程序很少会使用多线程把CPU计算资源吃完
    • 主要是利用多线程执行一些耗时的等待IO的操作,避免主线程被卡死,避免对用户造成不好的体验

2.Qt 线程的使用条件

  • 在Qt中,多线程常⽤于⽐较耗时的任务,或只有通过使⽤线程执⾏时才能正常运⾏的情况

3.创建线程的方法

1.方法一

  • 继承QThread类,重写run()函数 --> 多态
  • 步骤
    • ⾃定义⼀个类,继承于QThread,并且只有⼀个线程处理函数重写⽗类中的run()
    • 线程处理函数⾥⾯写⼊需要执⾏的复杂数据处理
    • 启动线程不能直接调⽤run(),需要使⽤对象来调⽤start()实现线程启动
      • start()底层就是调用OS API创建线程,新县城创建后自动执行run()
    • 线程处理函数执⾏结束后可以定义⼀个信号来告诉主线程
    • 最后关闭线程
  • 示例:定时器
    // thread.h
    class Thread : public QThread
    {Q_OBJECT
    public:Thread();// 重要的目的是重写父类的 run 方法.void run();signals:void Notify();
    };// thread.cpp
    void Thread::run()
    {// 每过一秒, 通过信号槽, 来通知主线程, 负责更新的界面内容for (int i = 0; i < 10; i++) {// sleep 本身是 QThread 的成员函数, 就可以直接调用sleep(1);// 发送一个信号, 通知主线程emit Notify();}
    }
    -------------------------------------------------------------------------
    // widget.h
    class Widget : public QWidget
    {// ...Thread thread;void Handle();
    };// widget.cpp
    // 构造函数中
    {// 连接信号槽, 通过槽函数更新界面connect(&thread, &Thread::Notify, this, &Widget::Handle);// 要启动一下线程.thread.start();
    }void Widget::handle()
    {// 此处修改界面内容.int value = ui->lcdNumber->intValue();value--;ui->lcdNumber->display(value);
    }
    

2.方法二

  • 继承QObject类,通过moveToThread(thread),交给thread执⾏
  • 函数原型moveToThread(QThread* targetThread)
  • 功能:将⼀个对象移动到指定的线程中运⾏
  • 步骤
    • ⾃定义⼀个类,继承于QObject
    • 创建⼀个⾃定义线程类的对象,不能指定⽗对象
    • 创建⼀个QThread类的对象,可以指定其⽗对象
    • 将⾃定义线程对象加⼊到QThread类的对象中使⽤
    • 使⽤start()启动线程
      • 调⽤start()只是启动了线程,但是并没有开启线程处理函数
      • 线程处理函数的开启需要⽤到信号槽机制
    • 关闭线程
  • 示例
    // thread.h
    class MyThread : public QObject
    {Q_OBJECT
    public:// ...void Thread();void SetFlag(bool flag = true);signals:void Notify();private:bool isStop() = false;
    };// thread.cpp
    void MyThread::Thread()
    {while(!isStop){QThread::sleep(1);emit Notofy();qDebug() << " ⼦线程号: " << QThread::currentThread();if(isStop){break;}}
    }void MyThread::SetFlag(bool flag)
    {isStop = flag;
    }
    -------------------------------------------------------------------------
    // widget.h
    class Widget : public QWidget
    {// ...MyThread* myThread;QThread* thread;signals:void StartSignal(); // 启动子线程信号private slot:void on_startPushbutton_clicked();void on_closePushbutton_clicked();void DelSignals();void DealClose();
    };// widget.cpp
    // 构造函数中
    {// 动态分配空间,不能指定⽗对象myThread = new MyThread();// 创建子线程thread = new QThread(this);// 将⾃定义的线程加⼊到⼦线程中myThread->moveToThread(thread);connect(myThread, &MyThread::Notify, this, &Widget::DelSignals);connect(this, &Widget::StartSignal, myThread, &MyThread::Thread);connect(this, &Widget::destroyed, this, &Widget::DealClose);
    }void MyWidget::on_startPushbutton_clicked()
    {if(thread->isRunning() == true){return;}// 启动线程但是没有启动线程处理函数thread->start();// 不能直接调⽤线程处理函数,直接调⽤会导致线程处理函数和主线程处于同⼀线程emit StartSignal();
    }void MyWidget::DelSignals()
    {static int i = 0;i++;ui->lcdNumber->display(i);
    }void MyWidget::on_closePushbutton_clicked()
    {if(thread->isRunning() == false){return;}myThread->SetFlag();thread->quit();thread->wait();
    }void MyWidget::DealClose()
    {delete myThread;on_closePushbutton_clicked();
    }
    

3.说明

  • 线程函数内部不允许操作UI图形界⾯,⼀般⽤数据处理
    • 只有主线程可以操作UI图形界面
  • connect()第五个参数表⽰的是连接的⽅式,且只有在多线程的时候才意义
  • connect()第五个参数Qt::ConnectionType,⽤于指定信号和槽的连接类型,同时影响信号的传递⽅式和槽函数的执⾏顺序
    • Qt::AutoConnection:会根据信号和槽函数所在的线程⾃动选择连接类型
      • 如果信号和槽函数在同⼀线程中,那么使⽤Qt:DirectConnection类型
      • 如果它们位于不同的线程中,那么使⽤Qt::QueuedConnection类型
    • Qt::DirectConnection:当信号发出时,槽函数会⽴即在同⼀线程中执⾏
      • 适⽤于信号和槽函数在同⼀线程中的情况
      • 可以实现直接的函数调⽤,但需要注意线程安全性
    • Qt::QueuedConnection:当信号发出时,槽函数会被插⼊到接收对象所属的线程的事件队列中,等待下⼀次事件循环时执⾏
      • 适⽤于信号和槽函数在不同线程中的情况,可以确保线程安全
    • Qt::BlockingQueuedConnection:与Qt:QueuedConnection类似
      • 但是发送信号的线程会被阻塞,直到槽函数执⾏完毕
      • 适⽤于需要等待槽函数执⾏完毕再继续的场景,但需要注意可能引起线程死锁的⻛险
    • Qt::UniqueConnection:这是⼀个标志,可以使⽤|与上述任何⼀种连接类型组合使⽤

4.关闭线程使用的方法

  • void terminate():直接关闭线程不等待线程任务结束
  • void quit():等待线程任务结束之后关闭线程

http://www.mrgr.cn/news/1901.html

相关文章:

  • 手机回合制策略游戏推荐:《文明6》手机版游戏分享
  • 搭建内网开发环境(三)|基于nexus搭建docker私服
  • 实现异形(拱形)轮播图
  • Processing中库和导出PDF内容
  • 【Android 笔记】Android APK编译打包流程
  • React前端面试每日一试 8.什么是React Portals?
  • Git基础知识
  • 【Linux】线程安全的单例模式 STL和智能指针的线程安全问题 其他常见的各种锁 读者写者模型(线程的周边话题)
  • go语言协程之间的同步
  • 解决k8s进入dashboard页面,浏览器提示连接不是私密链接的问题
  • 图像处理之 Gamma LUT
  • Ubuntu虚拟机服务器的搭建
  • qt-11基本对话框(消息框)
  • Python基础知识学习总结(五)
  • 夏季炎热,宠物化身掉毛大王,猫咪浮毛异味问题该如何解决?
  • C#:基本语法
  • Vue+SortableJs实现拖拽排序
  • 《深入理解 Java 接口》
  • 【区块链+金融服务】广电融汇通供应链金融平台 | FISCO BCOS应用案例
  • 如何从Mac 电脑恢复已删除的文件