QT中的循环定时器为什么不会阻塞UI线程呢?

大板牙花生 2020-10-18 09:11:41
这个底层原理是什么?
如果主线程UI中写while(true){}是会阻塞的,但是如果用QTIMER进行循环调用的话并不会阻塞,这个背后的事件机制,或是循环机制,什么都好是如何实现的?是不是基于WINDOWS的消息机制呢?
...全文
15309 19 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
  • 打赏
  • 举报
回复
1.人对卡顿的感受是,界面2s内没有响应 2.2s内,计算机已经可以处理几亿条指令了,所以你点一下鼠标或敲一下键盘,可以在毫秒级内处理完毕 3.操作系统是分时响应,在2s内可以并行处理很多执行代码,给人产生一种程序同时执行的错觉
_mervyn 2020-11-06
  • 打赏
  • 举报
回复
引用 17 楼 _mervyn 的回复:
只是往CustomList插个msg 能消耗几个cpu时间?? 况且,其他线程往主线程消息队列里放消息也不是频繁的操作。
另一方面,你可以再看下我上面给的getmessage的大致实现, 从自定义的消息队列里获取消息时,完全也可以先try lock,如果失败就优先转回头去尝试处理系统界面消息(PeekMessage) 。 这样也是一种优化。优先保证了界面消息的处理。
_mervyn 2020-11-06
  • 打赏
  • 举报
回复
引用 16 楼 大板牙花生 的回复:
其实我想的是。我可能问错了。是自定义的while循环会造成界面假死,但是QApplication::exec() 的循环却不会,这是什么机制?另外,往主线程的CustomList放入msg就可以了,这个是如何实现的 ,因为对主线程加锁的话不是会造成资源竞争吗?相互争夺锁资源不是会造成界面假死吗?
你还没看懂我说的吗? 那你真的知道“界面假死”是什么意思吗? 如果你不知道的话 你应该问的是: 什么样的情况会造成“界面假死”? 界面为什么会假死?诸如此类。。。 很简单,取决于平台,只要 界面消息能够及时得到处理,界面就不会死。 你的主线程只要一直能够处理 窗口创建、鼠标移动、鼠标点击、等等等等平台定义的ui相关的消息。 界面就不会死。 相反,假如你在某个界面消息处理时,比如你在鼠标移动消息处理里,写了个死循环啥都不干,那么其他界面消息得不到处理,界面自然就卡死了。 你可以参考上面的伪代码,说白了就是,主线程需要一直有机会去调用getmessage,这样界面就不会卡死。 所以,只要能一直及时处理界面消息就可以了。只有这一个条件。如果界面消息处理间隔在毫秒级别的时候,才会开始感觉出界面的卡顿。跟加不加锁有个毛线关系。 你所认为的加锁就会造成界面假死,那是你锁住的时间太长了吧,这锁如果都能把线程阻塞住毫秒级别的延迟,那这肯定是你粒度设计的不合理, 只是往CustomList插个msg 能消耗几个cpu时间?? 况且,其他线程往主线程消息队列里放消息也不是频繁的操作。
大板牙花生 2020-11-05
  • 打赏
  • 举报
回复
引用 15 楼 _mervyn 的回复:
[quote=引用 12 楼 大板牙花生 的回复:] 主线程的循环取数据是如何实现的?如果使用while的话一定会阻塞UI主线程的,所以就是想问这个机制是如何实现的?
。。。 没消息的时候,本来就是阻塞的啊。。。 你在想什么??。。。

// 主线程(UI线程) 事件循环(eventloop)
while(true)
{
	msg = getmessage(timeout)  // 这里就是阻塞的,直到有消息或超时
	if(msg)
	{
		processMsg(msg); // msg 可能是 窗口创建,鼠标移动,鼠标按下 ,计时器超时等等等。。。
						// 于是processMsg内部就走到你的mouseMoveEvent,mousePressEvent,timerEvent等等等等
	}
	//...
	if(exit_flag) //程序需要退出
	{
		break;
	}
}
至于定时器的做法,有很多方式,如果自己简单实现下的话,可以在上面写到的msg这个数据结构中加个时间戳信息,如果时间还未到,就不进行处理。 当然这还需要对getmessage这个消费者函数进行良好的设计,包括其消息队列的设计,简单点的话:

msg getmessage(int timeout)
{
	//获取系统消息,依赖平台api,获得平台相关特性的消息,比如窗口创建,鼠标移动,鼠标按下 等等等等
	msg = PeekMessage(....)
	if(msg)
	{
		//...
		//...
		return msg;
	}
	//获取自定义的其他事件
	msg = getFirstMsgFromCustomList();
	if(msg.timestamp > now) 
	{
		//还未超时,则重新放回后面
		appendMsgToList();
		//然后取下一个: msg = getFirstMsgFromList();
		//当然我这只是随便举的一个最简单的实现例子
		//真正好的实现,应该设计好的消息队列,比如时间轮
		//...
	}
	//...
	//...
}
设置定时器时,就只要往自己设计的CustomList中放入设置好timestamp的msg就可以了。当然定时器的精度就取决于传给getmessage的timeout的值了。 至于其他线程如何与主线程通信,也还是一样的,往主线程的CustomList放入msg就可以了。 再给你理一下: 主线程永远只在app.exec(...) 这个函数中,不会退出,exec内部就是一个无限循环,不断消费msg(或者说event,或者说signal)。每拿出来一个,就往对应的的onMsg(onEvent,slots)派送(即调用之)。 你如果还理解不了,你就随便找个你认为是在主线程中跑的代码,就比如说你重载了QWidget::mousePressEvent(QMouseEvent *event) 好了,这个函数在哪里被调用的,分析下当前的调用堆栈,代码为什么会跑去那里? 调用堆栈一直往上追溯,答案一定就是QApplication::exec() [/quote] 其实我想的是。我可能问错了。是自定义的while循环会造成界面假死,但是QApplication::exec() 的循环却不会,这是什么机制?另外,往主线程的CustomList放入msg就可以了,这个是如何实现的 ,因为对主线程加锁的话不是会造成资源竞争吗?相互争夺锁资源不是会造成界面假死吗?
_mervyn 2020-10-30
  • 打赏
  • 举报
回复
引用 12 楼 大板牙花生 的回复:
主线程的循环取数据是如何实现的?如果使用while的话一定会阻塞UI主线程的,所以就是想问这个机制是如何实现的?
。。。 没消息的时候,本来就是阻塞的啊。。。 你在想什么??。。。

// 主线程(UI线程) 事件循环(eventloop)
while(true)
{
	msg = getmessage(timeout)  // 这里就是阻塞的,直到有消息或超时
	if(msg)
	{
		processMsg(msg); // msg 可能是 窗口创建,鼠标移动,鼠标按下 ,计时器超时等等等。。。
						// 于是processMsg内部就走到你的mouseMoveEvent,mousePressEvent,timerEvent等等等等
	}
	//...
	if(exit_flag) //程序需要退出
	{
		break;
	}
}
至于定时器的做法,有很多方式,如果自己简单实现下的话,可以在上面写到的msg这个数据结构中加个时间戳信息,如果时间还未到,就不进行处理。 当然这还需要对getmessage这个消费者函数进行良好的设计,包括其消息队列的设计,简单点的话:

msg getmessage(int timeout)
{
	//获取系统消息,依赖平台api,获得平台相关特性的消息,比如窗口创建,鼠标移动,鼠标按下 等等等等
	msg = PeekMessage(....)
	if(msg)
	{
		//...
		//...
		return msg;
	}
	//获取自定义的其他事件
	msg = getFirstMsgFromCustomList();
	if(msg.timestamp > now) 
	{
		//还未超时,则重新放回后面
		appendMsgToList();
		//然后取下一个: msg = getFirstMsgFromList();
		//当然我这只是随便举的一个最简单的实现例子
		//真正好的实现,应该设计好的消息队列,比如时间轮
		//...
	}
	//...
	//...
}
设置定时器时,就只要往自己设计的CustomList中放入设置好timestamp的msg就可以了。当然定时器的精度就取决于传给getmessage的timeout的值了。 至于其他线程如何与主线程通信,也还是一样的,往主线程的CustomList放入msg就可以了。 再给你理一下: 主线程永远只在app.exec(...) 这个函数中,不会退出,exec内部就是一个无限循环,不断消费msg(或者说event,或者说signal)。每拿出来一个,就往对应的的onMsg(onEvent,slots)派送(即调用之)。 你如果还理解不了,你就随便找个你认为是在主线程中跑的代码,就比如说你重载了QWidget::mousePressEvent(QMouseEvent *event) 好了,这个函数在哪里被调用的,分析下当前的调用堆栈,代码为什么会跑去那里? 调用堆栈一直往上追溯,答案一定就是QApplication::exec()
androidqt 2020-10-28
  • 打赏
  • 举报
回复
你们学过单片机就会知道,其实用的内部定时器来计数,采用的中断所以不会阻塞的。
fly4free 2020-10-26
  • 打赏
  • 举报
回复
【UI线程】是独立的,一个窗口一个线程,就算【主界面】也是【UI线程】。

而 main 函数所在线程,也即主线程,也和 UI 线程相互独立的,不会阻塞 UI 线程。

main函数最后一般是 a.exec(); 内部就是 while 循环

大板牙花生 2020-10-24
  • 打赏
  • 举报
回复
引用 11 楼 翅膀又硬了 的回复:
[quote=引用 9 楼 大板牙花生 的回复:][quote=引用 8 楼 翅膀又硬了 的回复:][quote=引用 7 楼 大板牙花生 的回复:][quote=引用 6 楼 翅膀又硬了 的回复:][quote=引用 3 楼 大板牙花生 的回复:][quote=引用 1 楼 翅膀又硬了 的回复:]定时器的计时流程不在UI线程呗。
不管在不在主线程,是如何做到定时执行UI线程,但是UI线程却又不阻塞的情况?不是说其他线程修改UI线程的东西会发生错误吗[/quote]不要在Timer的槽函数里面修改UI,否则就会有你说的俩线程同时操作UI,很容易发生异常。在那发信号就行了。[/quote] 其实我关心 的就是你说的发信号的机制。不管是发信号,还是事件机制,或是消息机制,信号槽,就说核心是如何实现的?循环执行但是又不会阻塞UI,引发异常?[/quote]没看过Qt的实现方式。自己做这种可以这样实现:消息队列+互斥。 多个线程都可以往队列里面“放”数据,只有UI线程从队列取数据。[/quote] 问题是核心问题来了。UI如何从队列取数据?无非是循环定时或者是回调才能数据不是?回调不是涉及到了子线程调用主线程UI的问题?循环不是又涉及到了开头的问题?[/quote]不用回调。就是循环取数据,然后显示。[/quote] 主线程的循环取数据是如何实现的?如果使用while的话一定会阻塞UI主线程的,所以就是想问这个机制是如何实现的?
翅膀又硬了 2020-10-23
  • 打赏
  • 举报
回复
引用 9 楼 大板牙花生 的回复:
[quote=引用 8 楼 翅膀又硬了 的回复:][quote=引用 7 楼 大板牙花生 的回复:][quote=引用 6 楼 翅膀又硬了 的回复:][quote=引用 3 楼 大板牙花生 的回复:][quote=引用 1 楼 翅膀又硬了 的回复:]定时器的计时流程不在UI线程呗。
不管在不在主线程,是如何做到定时执行UI线程,但是UI线程却又不阻塞的情况?不是说其他线程修改UI线程的东西会发生错误吗[/quote]不要在Timer的槽函数里面修改UI,否则就会有你说的俩线程同时操作UI,很容易发生异常。在那发信号就行了。[/quote] 其实我关心 的就是你说的发信号的机制。不管是发信号,还是事件机制,或是消息机制,信号槽,就说核心是如何实现的?循环执行但是又不会阻塞UI,引发异常?[/quote]没看过Qt的实现方式。自己做这种可以这样实现:消息队列+互斥。 多个线程都可以往队列里面“放”数据,只有UI线程从队列取数据。[/quote] 问题是核心问题来了。UI如何从队列取数据?无非是循环定时或者是回调才能数据不是?回调不是涉及到了子线程调用主线程UI的问题?循环不是又涉及到了开头的问题?[/quote]不用回调。就是循环取数据,然后显示。
fly4free 2020-10-23
  • 打赏
  • 举报
回复
至少【代码线程】与【UI线程】是独立的

UI线程是基于一些数据来决定【如何显示数据】的

那么代码线程就是操作这些数据

两者都是操作数据,做好同步就可以了

简单说来就这样
青空飞羽 2020-10-22
  • 打赏
  • 举报
回复
你说的不阻塞是指while循环中可以执行定时器超时绑定的槽函数?
大板牙花生 2020-10-22
  • 打赏
  • 举报
回复
引用 2 楼 donwmufromdying 的回复:
内部都是eventloop机制
这个机制为什么不会阻塞ui线程?起码不会假死?
大板牙花生 2020-10-22
  • 打赏
  • 举报
回复
引用 8 楼 翅膀又硬了 的回复:
[quote=引用 7 楼 大板牙花生 的回复:][quote=引用 6 楼 翅膀又硬了 的回复:][quote=引用 3 楼 大板牙花生 的回复:][quote=引用 1 楼 翅膀又硬了 的回复:]定时器的计时流程不在UI线程呗。
不管在不在主线程,是如何做到定时执行UI线程,但是UI线程却又不阻塞的情况?不是说其他线程修改UI线程的东西会发生错误吗[/quote]不要在Timer的槽函数里面修改UI,否则就会有你说的俩线程同时操作UI,很容易发生异常。在那发信号就行了。[/quote] 其实我关心 的就是你说的发信号的机制。不管是发信号,还是事件机制,或是消息机制,信号槽,就说核心是如何实现的?循环执行但是又不会阻塞UI,引发异常?[/quote]没看过Qt的实现方式。自己做这种可以这样实现:消息队列+互斥。 多个线程都可以往队列里面“放”数据,只有UI线程从队列取数据。[/quote] 问题是核心问题来了。UI如何从队列取数据?无非是循环定时或者是回调才能数据不是?回调不是涉及到了子线程调用主线程UI的问题?循环不是又涉及到了开头的问题?
翅膀又硬了 2020-10-22
  • 打赏
  • 举报
回复
引用 7 楼 大板牙花生 的回复:
[quote=引用 6 楼 翅膀又硬了 的回复:][quote=引用 3 楼 大板牙花生 的回复:][quote=引用 1 楼 翅膀又硬了 的回复:]定时器的计时流程不在UI线程呗。
不管在不在主线程,是如何做到定时执行UI线程,但是UI线程却又不阻塞的情况?不是说其他线程修改UI线程的东西会发生错误吗[/quote]不要在Timer的槽函数里面修改UI,否则就会有你说的俩线程同时操作UI,很容易发生异常。在那发信号就行了。[/quote] 其实我关心 的就是你说的发信号的机制。不管是发信号,还是事件机制,或是消息机制,信号槽,就说核心是如何实现的?循环执行但是又不会阻塞UI,引发异常?[/quote]没看过Qt的实现方式。自己做这种可以这样实现:消息队列+互斥。 多个线程都可以往队列里面“放”数据,只有UI线程从队列取数据。
大板牙花生 2020-10-22
  • 打赏
  • 举报
回复
引用 6 楼 翅膀又硬了 的回复:
[quote=引用 3 楼 大板牙花生 的回复:][quote=引用 1 楼 翅膀又硬了 的回复:]定时器的计时流程不在UI线程呗。
不管在不在主线程,是如何做到定时执行UI线程,但是UI线程却又不阻塞的情况?不是说其他线程修改UI线程的东西会发生错误吗[/quote]不要在Timer的槽函数里面修改UI,否则就会有你说的俩线程同时操作UI,很容易发生异常。在那发信号就行了。[/quote] 其实我关心 的就是你说的发信号的机制。不管是发信号,还是事件机制,或是消息机制,信号槽,就说核心是如何实现的?循环执行但是又不会阻塞UI,引发异常?
翅膀又硬了 2020-10-22
  • 打赏
  • 举报
回复
引用 3 楼 大板牙花生 的回复:
[quote=引用 1 楼 翅膀又硬了 的回复:]定时器的计时流程不在UI线程呗。
不管在不在主线程,是如何做到定时执行UI线程,但是UI线程却又不阻塞的情况?不是说其他线程修改UI线程的东西会发生错误吗[/quote]不要在Timer的槽函数里面修改UI,否则就会有你说的俩线程同时操作UI,很容易发生异常。在那发信号就行了。
donwmufromdying 2020-10-21
  • 打赏
  • 举报
回复
内部都是eventloop机制
大板牙花生 2020-10-21
  • 打赏
  • 举报
回复
引用 1 楼 翅膀又硬了 的回复:
定时器的计时流程不在UI线程呗。

不管在不在主线程,是如何做到定时执行UI线程,但是UI线程却又不阻塞的情况?不是说其他线程修改UI线程的东西会发生错误吗
翅膀又硬了 2020-10-20
  • 打赏
  • 举报
回复
定时器的计时流程不在UI线程呗。

16,816

社区成员

发帖
与我相关
我的任务
社区描述
Qt 是一个跨平台应用程序框架。通过使用 Qt,您可以一次性开发应用程序和用户界面,然后将其部署到多个桌面和嵌入式操作系统,而无需重复编写源代码。
社区管理员
  • Qt
  • 亭台六七座
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧