QT延时/等待

一、阻塞型延时

阻塞的原理就是:在延迟期间,本线程的事件循环得不到处理

1.QThread类的sleep()

最简单的延时方法就是使用QThread类的sleep(n)、msleep(n)、usleep(n),这几个函数的不良后果就是,GUI会在延时的时间段内失去响应,界面卡死,所以这三个函数一般用在非GUI线程中。

QThread::msleep(50);//阻塞延时50ms

2.使用计时器,死等

void Delay_MSec_Suspend(unsigned int msec)
{    QTime _Time = QTime::currentTime().addMSecs(msec);while( QTime::currentTime() < _Time);
}

二、非阻塞型延时

原理无非就是利用事件循环,有两种原理:

1.处理本线程的事件循环

在等待中,不断强制刷新当前线程的事件循环,这样可以把事件队列中被阻塞的时间都处理掉,从而避免当前线程卡住(在主线程中,就是避免程序卡死)

void Delay_MSec(unsigned int msec)
{QTime _Time = QTime::currentTime().addMSecs(msec);while (QTime::currentTime() < _Time){QCoreApplication::processEvents(QEventLoop::AllEvents, 100);}}

QCoreApplication::processEvents(QEventLoop::AllEvents, 100);这条语句能够使程序在while等待期间,去处理一次本线程的事件循环,处理事件循环最多100ms必须返回本语句,如果提前处理完毕,则立即返回这条语句。这也就导致了该Delay_MSec函数的定时误差可能高达100ms。

2.使用子事件循环

创建子事件循环,在子事件循环中,父事件循环仍然是可以执行的

void Delay_MSec(unsigned int msec)
{QEventLoop loop;//定义一个新的事件循环QTimer::singleShot(msec, &loop, SLOT(quit()));//创建单次定时器,槽函数为事件循环的退出函数loop.exec();//事件循环开始执行,程序会卡在这里,直到定时时间到,本循环被退出
}

3.耗时代码的处理

假设有这样的应用情景:点击某个button之后,需要读入并处理一幅图像,需要耗时20秒才能处理完。

在这20s内,GUI会失去效应,界面上的任何元素都无法被点击,这种情况应该怎么办?
方法有两种:
1、用另一个线程去处理这个耗时任务;
2、在耗时任务中,不断地去处理本线程的事件循环,以保证GUI的及时响应;
这里重点说一下第2种,参考 Qt线程与事件循环

for(i=0; i < 1000000; i++)
{//QCoreApplication::processEvents(QEventLoop::AllEvents);    //去处理本线程的事件循环,避免本线程被堵塞QCoreApplication::processEvents(QEventLoop::AllEvents, 5);//如果不够频繁,可以增加第二参数来缓解卡顿for(j=0; j < 1000000; j++){//QCoreApplication::processEvents(QEventLoop::AllEvents);//处理事件循环,不建议放在这里,可能过于频繁doSomeThing();}
}

一般来说,processEvents()不宜被调用的过于频繁,也不宜被调用的不够频繁。过于频繁的话,一方面会使线程的响应更好,但另一方面会导致原本就耗时的任务变得更加耗时;不够频繁的话,显然可能会使GUI线程的响应变差,例如每500ms才被调用一次,那么GUI的事件循环就只能500ms才被处理一次,当然,这个问题可以通过设定processEvents()的第二个形参略微得到缓解,更好的做法是,保证被调的周期<200ms(再小一些更好,看程序需求),这样不至于肉眼可见的卡顿。

副作用:(特别注意!)
1、在点击按钮之后,这个20s的耗时任务开始执行,尚未执行完毕时,我们点击了GUI的关闭按钮,那么GUI会立即消失,但是这个耗时任务仍然会在后台执行,直到执行完毕,进程才会退出。解决办法:重写关闭事件,在关闭事件的函数中直接结束进程。

2、在点击按钮之后,这个20s的耗时任务开始执行,执行到第5秒时,我们再次点击了这个按钮,那么QT又会执行一个新的20s任务,这个新任务完成后,又会接着把第一个20s任务从上次被打断的第5秒继续执行。如果这个任务是可重入的,后果仅仅是被执行了两遍,如果任务不可重入,那情况就彻底糟糕了。解决办法:点击按钮后把这个按钮disable掉,执行完再enable


在嵌入式开发中,经常会遇到这样一种场景,需要轮询获取某个设备的状态
例如:某个场景需要不断获取 激光测距模块物理设备 的测距值,但是激光测距模块此时不一定可以获取到正常的值,就需要轮询,不断的获取,直到获取到一次正常的值之后,打破轮询;或是达到超时时间打破轮询

1.使用QTimer进行定时检查

#include <QCoreApplication>
#include <QTimer>
#include <QDebug>class LaserDistanceSensor : public QObject
{Q_OBJECT
public:LaserDistanceSensor(QObject* parent = nullptr): QObject(parent){m_pollTimer = new QTimer(this);m_pollTimer->setInterval(200); //每200ms检查一次,距离值是否有效connect(m_pollTimer, &QTimer::timeout, this, &LaserDistanceSensor::checkDistance);}//开始轮询void startPolling(){if (!m_pollTimer->isActive()){m_pollTimer->start();QTimer::singleShot(5000, this, &LaserDistanceSensor::pollingTimeout); // 设置5秒超时,打破轮询}}// 模拟获取激光测距模块的值的函数double getDistanceValue(){return (rand() % 10 > 5) ? 1.0 : -1.0;}//检查是否已经获取到有效激光测距值void checkDistance(){double distance = getDistanceValue();if (distance > 0){qDebug() << "有效的激光测距值:" << distance;m_pollTimer->stop(); // 停止轮询}}//轮询超时,未获取到有效值void pollingTimeout(){if (m_pollTimer->isActive()){qDebug() << "超时未获取有效激光测距值";m_pollTimer->stop();}}private:QTimer* m_pollTimer = nullptr;
};

外部调用 startPolling()函数就可以开始轮询了,达到条件就会打破轮询,请注意,如果startPolling()函数被调用的线程与LaserDistanceSensor 对象创建的线程不一致,QTimer就无法运行,因为QTimer的运行依赖事件循环,且只能在QTimer对象创建的线程中的事件循环中运行;

非同一线程时,Qt报错:

QObject::startTimer: Timers cannot be started from another thread
QObject::startTimer: Timers can only be used with threads started with QThread

这种情况时,我们可以使用信号槽的方式解决此问题,比如添加一个信号,在槽函数中调用startPolling(),就可以让QTimer顺利运行,信号可以在不具有事件循环的线程中发送,只要接收对象所处的线程有事件循环即可,发信号和QApplication::postEvent的作用机制类似,都是朝receiver的事件队列中放入一个事件/信号,等待receiver对象的事件循环来处理该事件/信号

class LaserDistanceSensor : public QObject
{Q_OBJECT
public:LaserDistanceSensor(QObject* parent = nullptr): QObject(parent){m_pollTimer = new QTimer(this);m_pollTimer->setInterval(200); //每200ms检查一次,距离值是否有效connect(m_pollTimer, &QTimer::timeout, this, &LaserDistanceSensor::checkDistance);connect(this, &LaserDistanceSensor::sglStartPolling, this, &LaserDistanceSensor::startPolling);}void startPollingInOtherThread(){emit sglStartPolling();}//开始轮询void startPolling(){if (!m_pollTimer->isActive()){m_pollTimer->start();QTimer::singleShot(5000, this, &LaserDistanceSensor::pollingTimeout); // 设置5秒超时,打破轮询}}// 模拟获取激光测距模块的值的函数double getDistanceValue(){return (rand() % 10 > 5) ? 1.0 : -1.0;}//检查是否已经获取到有效激光测距值void checkDistance(){double distance = getDistanceValue();if (distance > 0){qDebug() << "有效的激光测距值:" << distance;m_pollTimer->stop(); // 停止轮询}}//轮询超时,未获取到有效值void pollingTimeout(){if (m_pollTimer->isActive()){qDebug() << "超时未获取有效激光测距值";m_pollTimer->stop();}}signals:void sglStartPolling();private:QTimer* m_pollTimer = nullptr;
};

外部调startPollingInOtherThread()函数即可。

2.使用While进行轮询

伪代码如下,类似于非阻塞延时的第一种形式

    bool validValueReceived = false;// 设置超时时间为5秒const int timeoutSeconds = 5;QTime startTime = QTime::currentTime().addSecs(timeoutSeconds);while (!validValueReceived){// 检查是否超时QTime currentTime = QTime::currentTime();if (startTime.secsTo(currentTime) >= timeoutSeconds){qDebug() << "超时未获取有效激光测距值";break;}// 获取激光测距模块的值double distance = getDistanceValue();if (distance > 0){qDebug() << "有效的激光测距值:" << distance;validValueReceived = true;}// 处理事件,确保应用程序响应QCoreApplication::processEvents();}

参考:
QEventLoop
Qt线程基础

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/722915.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《 前端 vs. 后端:挑战与机遇的对决》

前言 前端开发和后端开发是构建网站、应用程序和其他软件的两个主要方面。它们各自负责不同的任务和功能。 前端开发: 定义:前端开发是指构建用户直接与之交互的网站或应用程序的过程。前端开发主要关注于用户界面和用户体验。技术栈:前端开发通常涉及使用 HTML、CSS 和 Ja…

组基轨迹建模 GBTM的介绍与实现(Stata 或 R)

基本介绍 组基轨迹建模&#xff08;Group-Based Trajectory Modeling&#xff0c;GBTM&#xff09;&#xff08;旧名称&#xff1a;Semiparametric mixture model&#xff09; 历史&#xff1a;由DANIELS.NAGIN提出&#xff0c;发表文献《Analyzing Developmental Trajectori…

7.1.3 Selenium的用法2

目录 1. 切换 Frame 2. 前进后退 3. 对 Cookies 操作 4. 选项卡管理(了解) 5. 异常处理 6. 反屏蔽 7. 无头模式 1. 切换 Frame 我们知道网页中有一种节点叫作 iframe&#xff0c;也就是子 Frame&#xff0c;相当于页面的子页面&#xff0c;它的结构和外部网页的结构完全…

android高级面试题及答案,已拿offer

一、java相关 java基础 1、java 中和 equals 和 hashCode 的区别 2、int、char、long 各占多少字节数 3、int 与 integer 的区别 4、谈谈对 java 多态的理解 5、String、StringBuffer、StringBuilder 区别 6、什么是内部类&#xff1f;内部类的作用 7、抽象类和接口区别 java高…

SkyWalking链路追踪上下文TraceContext的traceId生成的实现原理剖析

结论先行 【结论】 SkyWalking通过字节码增强技术实现&#xff0c;结合依赖注入和控制反转思想&#xff0c;以SkyWalking方式将追踪身份traceId编织到链路追踪上下文TraceContext中。 是不是很有趣&#xff0c;很有意思&#xff01;&#xff01;&#xff01; 【收获】 skywal…

【Mining Data】收集数据(使用 Python 挖掘 Twitter 数据)

@[TOC](【Mining Data】收集数据(使用 Python 挖掘 Twitter 数据)) 具体步骤 第一步是注册您的应用程序。特别是,您需要将浏览器指向 http://apps.twitter.com,登录 Twitter(如果您尚未登录)并注册新应用程序。您现在可以为您的应用程序选择名称和描述(例如“Mining Demo”…

【华为OD机试】密码输入检测【C卷|100分】

【华为OD机试】-真题 !!点这里!! 【华为OD机试】真题考点分类 !!点这里 !! 题目描述 给定用户密码输入流 input,输入流中字符 < 表示退格,可以清除前一个输入的字符, 请你编写程序,输出最终得到的密码字符,并判断密码是否满足如下的密码安全要求。 密码安全要求如下…

Unity或者其他程序启动C#的控制台程序传递参数出错

Unity或者其他程序启动C#的控制台程序传递参数出错 主机启动代码 string exePath path ProConst.ProgramPath_GenerateReportExe;//设置exe启动的路径 string data JsonConvert.SerializeObject(GameManager.Instance._UserTrainingDataEntities);//将对象转成json Proces…

未来已来!AI大模型引领科技革命

未来已来&#xff01;AI大模型正以惊人的速度引领着科技革命。随着科技的发展&#xff0c;人工智能在各个领域展现出了非凡的能力和潜力&#xff0c;大模型更是成为了科技领域的明星。从自然语言处理到图像识别&#xff0c;从智能推荐到语音识别&#xff0c;大模型的应用正在改…

基于ZYNQ PS-SPI的Flash驱动开发

本文使用PS-SPI实现Flash读写&#xff0c;PS-SPI的基础资料参考Xilinx UG1085的文档说明&#xff0c;其基础使用方法是&#xff0c;配置SPI模式&#xff0c;控制TXFIFO/RXFIFO&#xff0c;ZYNQ的IP自动完成发送TXFIFO数据&#xff0c;接收数据到RXFIFO&#xff0c;FIFO深度为12…

word转PDF的方法 简介快速

在现代办公环境中&#xff0c;文档格式转换已成为一项常见且重要的任务。其中&#xff0c;将Word文档转换为PDF格式的需求尤为突出&#xff0c;将Word文档转换为PDF格式具有多方面的优势和应用场景。无论是为了提高文档的可读性和稳定性、保障文档的安全性和保护机制、还是为了…

IDEA运行大项目启动卡顿问题

我打开了很多项目&#xff0c;然后又启动了一个大型项目时&#xff0c;启动到一半&#xff0c;弹出一个窗口&#xff0c;告诉我idea内存不够&#xff0c;怎么解决这个问题&#xff1f; 1、先把多余的项目关掉&#xff0c;再启动这个大项目&#xff0c; 2、如果还是不行就去修改…

一文帮助快速入门Django

文章目录 创建django项目应用app配置pycharm虚拟环境打包依赖 路由传统路由include路由分发namenamespace 视图中间件orm关系对象映射操作表数据库配置model常见字段及参数orm基本操作 cookie和sessiondemo 创建django项目 指定版本安装django&#xff1a;pip install django3.…

Unity使用UnityWebRequest读取音频长度不对的解决方法

在开发的过程中碰到这样一个问题&#xff0c;有的音频文件通过UnityWebRequest读取出来后&#xff0c;AudioClip的Length会不对&#xff0c;比如本身有7秒&#xff0c;读出来只有3秒。代码如下&#xff1a; IEnumerator TestEnumerator() {UnityWebRequest www UnityWebReque…

MySQL查找树形结构中某个节点及其子节点

问题 设计表结构存储树形结构数据时&#xff0c;一般使用 parentId 来记录当前节点的父id。 表结构如下所示&#xff08;以MySQL为例&#xff09; create table test (id varchar(30) collate utf8mb4_general_ci default not nullprimary key,name varch…

gitlab的安装

1、下载rpm 安装包 (1)直接命令下载 wget https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/gitlab-ce-11.6.10-ce.0.el7.x86_64.rpm&#xff08;2&#xff09;直接去服务器上下载包 Index of /gitlab-ce/yum/el7/ | 清华大学开源软件镜像站 | Tsinghua Open Source…

Netty (10)-WebSocket

搭建服务基本配置参考第1篇。本篇仅介绍实现WebSocket服务器的关键代码 initChannel public void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline ch.pipeline(); pipeline.addLast(new HttpServerCodec()); pipeline.addLast(new Ht…

【图论】图的遍历 - 构建领接表(无向图)

文章目录 例题&#xff1a;受限条件下可到达节点的数目题目描述代码与注释模板抽象 例题&#xff1a;受限条件下可到达节点的数目 题目链接&#xff1a;2368. 受限条件下可到达节点的数目 题目描述 代码与注释 func reachableNodes(n int, edges [][]int, restricted []int)…

数学建模介绍

一、引言 数学建模&#xff0c;作为一种跨学科的方法论&#xff0c;已经逐渐成为科学研究、工程技术、社会经济管理等领域不可或缺的工具。简而言之&#xff0c;数学建模就是通过建立数学模型来模拟和解决实际问题。在这个过程中&#xff0c;我们需要将实际问题的复杂性和非线…

网络编程:select、poll

.1、select完成TCP并发服务器 程序代码&#xff1a; #include <myhead.h> #define SER_IP "192.168.125.234" //服务端IP #define SER_PORT 8888 //服务端端口号int main(int argc, const char *argv[]) {//1.创建用于连接的套接字int sfds…