Linux Qt 开发之多线程

2023-05-16

Qt 应用程序中所有的界面响应事件都在一个主线程中运行,当我们去调用QApplication对象的exec()方法时,Qt就不断去循环查询当前的事件队列中有没有事件发生,如果有则转去执行对应的槽函数,如果将过多的业务逻辑及耗时的操作放到当前的主线程中去执行,则界面的响应事件就无法及时得到响应,整个界面就会卡顿。

一般Qt应用程序的开发分为界面和后台两部分,在主线程中执行界面显示相关的操作,其他的业务逻辑则放到线程中去作为后台程序去执行,线程和线程及线程和主线程之间通过信号和槽进行通讯。

多线程开发中,常用的组件有线程、消息队列、信号量、互斥锁。一般一个线程维护一个消息队列,线程不断的查询或阻塞等待直到队列中有消息,则去执行相应的操作,并使用信号量进行线程间的同步,使用互斥锁保护线程间的共享资源。在Qt中也有线程、信号量、互斥锁组件,但消息队列则换成了信号&&槽机制,Qt中的每个线程自身单独维护一个事件队列,我们将不同的线程对象的信号和槽关联在一起,当事件发生时,对应信号则从一个线程发送到另一个线程的事件队列中,当前线程获取信号后,执行对应的槽函数,执行完成后再次进入事件监测循环中。

Qt实现多线程有两种方式,一种是基于QThread 类,使用时定义一个新的类继承QThread类,并重写QThread的run()方法,需要注意的是,这种方式只有run()方法是在新线程中执行的,其他的方法还是在创建对象的线程中执行。这种方法官方不推荐使用,已经被淘汰了,我们仅了解一下就可以。第二种方法是基于QObject类,使用时定义一个新的类继承QObject类,调用新类对象的moveToThread()方法绑定当前对象到一个线程中。该种方式新类的所有方法都被放在新的线程中执行。

1. QThread对象的创建

定义一个新类 继承 QObject类(新类的实例化不能设置任何父对象)。

实例化一个QThread对象。

实现新类中的槽函数。

QObject子类对象通过moveToThread将自己放到线程QThread对象中。

调用QThread对象的start函数启动线程。​​​​​​​

关联不同对象的信号和槽到新的类实例上。

2. QThread对象的销毁

 先把QObject在线程循环中释放(使用QObject::deleteLater函数),然后QThread::quit,然后QThread::wait。

1.   把线程的finished信号和object的deleteLater槽进行关联,线程退出时,释放QObject对象申请的内存空间。

2. 在析构函数中调用QThread::quit方法退出线程的事件循环。

3. 在析构函数中调用QThread::wait 回收线程资源。

我们看一下官方代码:

class Worker : public QObject
{
    Q_OBJECT
public slots:
    void doWork(const QString ¶meter) {
        QString result;
        emit resultReady(result);
    }
signals:
    void resultReady(const QString &result);
};
class Controller : public QObject
{
    Q_OBJECT
    QThread workerThread;  //实例化线程对象
public:
    Controller() {
        Worker *worker = new Worker; //实例化新类(不能指定父对象)
        worker->moveToThread(&workerThread);
        connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater); //释放QObject资源
        connect(this, &Controller::operate, worker, &Worker::doWork);
        connect(worker, &Worker::resultReady, this, &Controller::handleResults);
        workerThread.start();
    }
    ~Controller() {
        workerThread.quit();  //退出线程事件循环
        workerThread.wait();  //回收线程资源
    }
public slots:
    void handleResults(const QString &);
signals:
    void operate(const QString &);
};

3. 线程的同步-----信号量QSemphore

互斥锁保护的资源同一时刻只能有一个线程能够获取使用权,有些资源是可以限定多个线程同时访问,那么这个时候可以使用信号量。在Qt中信号量为QSemaphore。

QSemaphore sem(2) //实例化信号量对象,并设定初始值为2.

sem.acquire();  //获取信号量,获取不到阻塞等待

sem.tryAcquire();  //获取信号量,获取不到立即返回

semaphore.release(); //释放信号量

4. 线程共享资源的保护----互斥锁QMutex、QMutexLocker

QMutex类提供了一个保护一段临界区代码的方法,他每次只允许一个线程访问这段临界区代码。

QMutex mutex; //实例化互斥对象

mutex.lock(); //对共享资源加锁,获取不到,阻塞等待

mutex.tryLock(int timeout = 0) //获取不到锁,超时返回,time==0,立即返回。

mutex.unlock(); //释放锁

Qt提供了QMutexLocker类何以简化互斥量的处理,它在构造函数中接受一个QMutex对象作为参数并将其锁定,在析构函数中解锁这个互斥量。

QMutex mutex;
void func()
{
    QMutexLocker locker(_mutex);       //func()执行完后,自动释放锁。
}

5. 读写锁QReadWriteLocker、QReadLocker、QWriteLocker

不常用,暂不作说明。

6. QWaitCondition允许一个线程在一定条件下唤醒其他线程,生产这消费这模型

 void Producer::run()  //生产者
   {  
       for (int i = 0; i < DataSize; ++i) {  
           mutex.lock();  
           if (usedSpace == BufferSize)  
               bufferIsNotFull.wait(&mutex);  
           buffer[i % BufferSize] = "MING"[uint(rand()) % 4];  
           ++usedSpace;  
           bufferIsNotEmpty.wakeAll();  
        mutex.unlock();  
        }  
   }

void Consumer::run()   //消费者
 {  
     forever {  
         mutex.lock();  
         if (usedSpace == 0)  
             bufferIsNotEmpty.wait(&mutex);  
         cerr << buffer[i % BufferSize];  
         --usedSpace;  
         bufferIsNotFull.wakeAll();  
      mutex.unlock();  
  }  
  cerr << endl;  
  }

 bool wait ( QMutex * mutex, unsigned long time = ULONG_MAX ); 

这个函数做下说明,该函数将互斥量解锁并在此等待,它有两个参数,第一个参数为一个锁定的互斥量,第二个参数为等待时间。

调用wait()操作的线程使得作为参数的互斥量在调用前变为锁定状态,然后自身被阻塞变成为等待状态直到满足以下条件:

其他线程调用了wakeOne()或者wakeAll()函数,这种情况下将返回"true"值。

第二个参数time超时(以毫秒记时),该参数默认情况是ULONG_MAX,表示永不超时,这种情况下将返回"false"值。

wait()函数返回前会将互斥量参数重新设置为锁定状态,从而保证从锁定状态到等待状态的原则性转换。

7. QTimer定时器
头文件  #include<QTimer>

QTimer *timer = new QTimer(this);      //this 指定父对象,可自动回收

connect(timer, SIGNAL(timeout()), this, SLOT(update()));

timer->start(1000);


   
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux Qt 开发之多线程 的相关文章

  • TensorRT cuda8.0 cudnn 7.0.5 tar包安装

    总体步骤参考这篇文章 https zhuanlan zhihu com p 35468450 1 准备环境 TensorRT 依赖cuda和cudnn xff0c 且根据下载的TensorRT版本 xff0c 需要严格保证cuda和cudn
  • caffe模型TensorRT部署实践(一)

    参考代码 TensorRT安装包下的samples sampleMNIST sampleMNIST cpp 1 设置使用的gpu id xff0c 如果不设置 xff0c 默认使用第0块 cudaSetDevice 3 set device
  • Ubuntu关于串口的操作(查看串口信息、串口助手、串口权限)

    一 查看本机串口信息 1 串口是否在使用 串口在 dev 目录中 xff0c 查看串口是否在使用 xff0c 可以用命令 ls l dev ttyUSB0 说明 xff1a ls l xff1a 是查看目录的命令 xff0c 直接用ls或l
  • 基于caffe的量化模型训练与部署——训练篇

    为什么需要量化 xff1f 我们知道 xff0c cnn网络的前向计算瓶颈主要集中在卷积层 xff0c 而卷积层的实质是大量的浮点数相乘 相加等运算操作 xff0c 大量的浮点数计算限制了模型在低处理器或移动端等设备中的部署 如果能将浮点运
  • get/post简单请求示例

    安装了django后使用django admin startproject demoapi创建django项目 xff0c cd demopai进入项目目录内 xff0c 创建apppython manage py startapp api
  • 什么是请求报文和响应报文?

    在了解请求报文和响应报文之前 咱们先了解下什么是HTTP协议 http协议 网络传输协议 协议 规定网络数据传输格式 http协议组成 请求报文 43 响应报文 浏览器发请求 必须是 xff1a 请求报文 服务器响应 必须是 xff1a 响
  • std::vector

    文章目录 问题处理记录std vector查找指定元素C 43 43 概念优点缺点使用 vector声明和初始化vector的基本操作二维向量vector的begin end 和 front back 的区别示例应用场景 C 43 43 中
  • 4、认识一下堆和栈

    文章目录 一 前言二 程序的内存分配方式 一 前言 因为FreeRTOS对堆和栈的理解很重要 xff0c 所以学习Freertos xff0c 先简单了解一下内存的存储方式 xff0c 堆和栈是最重要的们主要是那个返回地址 xff01 xf
  • TCP/IP详解

    前言 精通 TCP IP xff0c 熟练使用 Socket 进行网路编程 看到这句话 xff0c 有没有感到很熟悉呀 xff1f 相信很多人在投递简历的时候都看到过这条要求 xff0c 很多人会觉得我们在实际开发中一般用不到这些知识 xf
  • C语言 epoll实现IO多路复用

    友链 gcc 1 c o 1 lpthread ctrl 43 f搜索服务端代码和客户端代码获取代码 该方法也可以实现并发服务器 服务端代码 span class token comment 使用内存映射可以拷贝文件 span span c
  • 1.2 struct和class最本质区别_马鸿凯_新浪博客

    C 43 43 中的struct对C中的struct进行了扩充 xff0c 它已经不再只是一个包含不同数据类型的数据结构了 xff0c 它已经获取了太多的功能 struct能包含成员函数吗 xff1f 能 xff01 struct能继承吗
  • 在 VSCode 终端里无法使用命令

    问题原因 xff1a 导致 vscode 的终端里执行命令报错 xff0c 但在 cmd 里能正常执行 的原因是 vscode 终端不能使用命令了 解决思路 xff1a 令 vscode 终端能够使用命令 解决方案 xff1a 1 右击 V
  • 使用 FFT 进行频谱分析

    下面的示例说明了如何使用 FFT 函数进行频谱分析 FFT 的一个常用场景是确定一个时域噪声信号的频率分量 首先创建一些数据 假设是以 1000 Hz 的频率对数据进行的采样 首先为数据构造一条时间轴 xff0c 时间范围从 t 61 0
  • CRC-16 / MODBUS 校验计算方法

    CRC 16 MODBUS xff1a 1 xff09 CRC寄存器初始值为 FFFF xff1b 即16个字节全为1 xff1b 2 xff09 CRC 16 MODBUS的多项式A001H 1010 0000 0000 0001B H
  • ZYNQ下驱动ADF5355试验成功

    前两个集BLOG分别给出了ADF5355接口的VERILOG设计实现以及设置频率的C语言算法 xff0c 我今天试验了以 xff0c 试验成功了 首先很重要的是ADF5355寄存器配置的设置 xff1a 可以在这个基础上修改输入以及输入频率
  • 基于sdrpi的openwifi实践1:编译openwifi-hw

    www github com open sdr openwifi hw是openwifi的硬件项目 hw应该就取haraware之意思 xff0c 包含就是支持多个板子的项目以及openwifi作者写的一系列的简洁好用工具软件 我在做sdr
  • lmx2594的verilog驱动

    应朋友请求写了一个纯VERILOG的LMX2594的配置 首先写一个SPI的接口 xff1a lmx2594 spi master lmx2594 spi master clk rst W0R1 pin mosi pin sclk pin
  • ZC706P+ADRV9009连接RADIOVERSE详解之三

    做好SD卡映像 xff0c 连接好硬件之后 xff0c 我们就可以尝试软件操作了 步骤1 xff1a 设置好网络 打开软件界面我们看到 xff0c 板子默认的地址为192 168 1 10 端口号为55555 我们一定也设置跟板子连接的以太
  • PCIE的AXI LITE MASTER端使用

    参考文章 xff1a https blog csdn net mcupro article details 121668833 spm 61 1001 2014 3001 5501 近期计划设计一个PCIE接口的使用AD9361芯片的SDR
  • ZC706P试验PL_DDR3内存条的步骤方法

    ZC706P 板卡完全兼容XILINX官方的ZC706 当然也支持PL外挂的1G的DDR3内存条 xff0c 这个片BLOG我提供从官方下载的一个文档和一个项目 xff0c 演示一下验证DDR3的步骤 步骤1 xff1a 准备好板子 xff

随机推荐

  • 【ZedBoard实验随笔】OV7670 摄像头 简单总结

    1 xff0c OV7670 有一大堆寄存器需要配置 xff0c 这些配置可以参考别人已经做好的代码 2 xff0c OV7670 设置为640 480 16的分辨率 xff0c 30帧每秒 xff0c 就是30FPS xff0c 每一个R
  • AXI STREAM ,AXIS总线的理解

    全兼容ZEDBOARD 开发板子 SYSCLK TAOBAO COM 1 xff0c VALID和READY 是所有AXI总线必须有的 xff0c VALID是MASTER告诉SLAVE数据已经展现在了总线上了 xff0c 你可以取走了 x
  • ZYNQ7 DACHE的重要操作

    Flush the SrcBuffer before the DMA transfer in case the Data Cache is enabled Xil DCacheFlushRange u32 TxPacket MAX PKT
  • OV7670摄像采集图像在VGA上显示的分析和实现,精确到每个时钟,每行代码。

    OV7670使用RGB565模式 xff0c 30FPS xff0c 采集分辨率为640X480 xff0c 保存在ZYNQ7芯片7Z020的内部BRAM VGA控制器在读出端读出数据 xff0c 并用12BIT的色彩深度现在VGA接口屏幕
  • Ubuntu 20.04 python2安装pip

    Ubuntu 20已经无法通过apt来安装python2的pip2了 xff0c 只能安装python3的pip 方法 先确保你有python2 暂时还是可以用apt安装python2 sudo apt install python2 下面
  • PetaLinux 2019.1详细安装步骤以及所需要文件

    这里主要根据UG1144文档 xff0c 这两天成功安装使用了PetaLinux的最新版本2019 1 一 xff0c 在虚拟机里安装Ubuntu16 04 1 xff0c 选Ubuntu 操作系统的版本是16 04 我最初尝试过18版本的
  • STC芯片在Keil中的添加与使用

    一 使用官方烧录工具 stc isp v6 88K exe在Keil中添加STC芯片 此烧录工具可在STC官网上下载 二 打开STC ISP软件 xff0c 选择右侧 Keil仿真设置 栏 三 选择 添加型号和头文件到Keil中 添加STC
  • boost 与 C++11 比较

    先不说boost的初始化表是多么怪异的形式了 c 43 43 11用大括号一括起来就OK了 auto 和 boost auto 差别太大了 xff0c 必须得告诉boost auto宏一个类型 xff0c 而非编译器本身的推断 xff0c
  • C++——struct和class的区别

    在C语言中struct是一种数据类型 xff0c 那么就肯定不能定义函数 xff0c 所以在面向c的过程中 xff0c struct不能包含任何函数 否则编译器会报错 而在C 43 43 中 xff0c 其特性也有了新发展 xff0c 就拿
  • roslaunch使用示例

    本文是看了学长ROS 的 roslaunch 中 xml 相关要点 文章之后做了小改动 xff0c 主要是对 roslaunch 文件的简单总结 需要完成的前序工作是已经创建了 catkin 工作空间 xff1b 创建了 catkin 程序
  • 四旋翼的运动原理及几个飞行问题

    四旋翼的运动及原理 飞行的问题 调试 四旋翼飞行器通过调节四个电机转速来改变旋翼转速 xff0c 实现升力的变化 xff0c 从而控制飞行器的姿态和位置 四旋翼飞行器是一种六自由度的垂直升降机 xff0c 但只有四个输入力 xff0c 同时
  • 以太网数据包长度限制

    我们使用以太网进行数据传输 xff08 UDP TCP xff09 xff0c 数据包的大小是有限制的 xff0c 并不是多长都可以 TCP IP协议从下到上包含 xff1a 链路层 IP层 传输层 应用层 链路层 xff1a 链路层的数据
  • 串口接收完整一帧数据包的3种方法

    本文介绍3种使用串口接受一帧完整数据包的方法 xff0c 串口接收数据是字节接收的 xff0c 串口每接收1字节数据 xff0c 产生一个串口中断 xff0c 我们在中断中将接收到的数据存放到buf中进行保存 xff0c 但是数据的发送和接
  • 补码原码转换

    本文主要讲述补码和原码之间的换算 1 原码 转 补码 xff1a 正整数的补码是它本身 xff0c 即 1的原码是0000 0001 xff0c 补码 还是 0000 0001 负整数的补码是符号位不变 xff0c 其余位按位取反 xff0
  • 有符号数与无符号数之间的转换

    本文讲解有符号数和无符号数之间的转换 针对同一数据类型有符号数和无符号数之间的转换 xff0c 例如 int 和 unsigned int 之间和 char 和 unsigned char 之间 这种情况下 xff0c 二者之间的数据类型长
  • Ubuntu由于修改etc/profile文件导致开机无法登陆问题解决办法(仅供参考)

    在安装jdk或者arm linux gcc等开发工具时需要修改profile xff0c 一不小心导致无法正常登录系统 xff0c 这时候可以试试用以下办法解决 xff1a 1 进入ubuntu登录页面 xff0c 按alt 43 ctrl
  • 一文看懂,用示波器查看串口UART的波形,什么是数据位、起始位、奇偶校验位

    先介绍一些概念 起始位 先发出一个逻辑 0 的 信号 xff0c 表示传输数据的开始 校验位 xff1a 数据位加上这一位后 xff0c 使得 1 的位数应为偶数 偶校验 或奇数 奇校验 xff0c 以此来校验数据传送的正确性 就比如传输
  • Could not get lock /var/lib/dpkg/lock 问题解决方法

    今天在ubuntu上安装sqlite 时 xff0c 执行命令sudo apt get y install sqlite3 时 xff0c 系统提示 xff1a Could not get lock var lib dpkg lock op
  • Linux 应用开发之系统调用

    系统调用 xff08 System Call xff09 是操作系统提供给用户程序调用的一组 特殊 函数接口 Linux 系统分为内核空间和用户空间 xff0c 用户空间只能通过系统调用接口与内核空间进行通信 Linux 系统提供了两种方式
  • Linux Qt 开发之多线程

    Qt 应用程序中所有的界面响应事件都在一个主线程中运行 xff0c 当我们去调用QApplication对象的exec 方法时 xff0c Qt就不断去循环查询当前的事件队列中有没有事件发生 xff0c 如果有则转去执行对应的槽函数 xff