注意:本文翻译自
http://developer.qt.nokia.com
中的
QtTimers
,中文译文见
简体中文版
,如果你对翻译wiki感兴趣,请参考Wiki中文帮助
定时器的 API
Qt 提供了两套 计时器的 API
- QObject::startTimer - 创建一个由QObject的任意子类使用的循环定时器,并返回定时器的ID。当定时时间到时它会收到一个 QEvent::Timer,可以通过覆盖QObject::timerEvent(QTimerEvent*)进行处理。该QTimerEvent参数包含定时器ID,当QObject使用多个定时器时可以进行匹配选择。 QObject::killTimer(id) 可用于停止和解除计时器。
- QTimer - QTimer 是一个当定时时间到时发射 timeout() 信号的 QObject。它简单地使用 QObject::startTimer() 并在事件处理器 QObject::timerEvent() 中发射 timeout() 信号。
QAbstractEventDispatcher
QtEventProcessing
提供了平台原生事件如何被Qt处理的概述信息。
QAbstractEventDispatcher 是事件/消息泵的接口。Qt创建的每一个线程中都存在一个事件派发器(event dispatcher)。为实现定时器它要求
registerTimer
和
unregisterTimer
必须被实现。
它有两个版本的 registerTimer (一个虚函数一个非虚)。两个版本都接受一个定时器所关联的QObject作为参数。非虚的registerTimer(夸平台)分配一个唯一的定时器ID,然后调用虚函数 registerTimer 一平台相关的方式来真正完成定时器的注册或创建。通过这种方法,平台的事件派发其维护着每个QObject对象关联的定时器的列表。
QAbstractEventDispatcher::registeredTimers
可被用来查询QObject对象所关联的定时器的列表。
在底层,一旦你注册一个定时器,它的时间间隔将不能被改变而且应当被看作不可变的。一个高层的API可以创建一个新的定时器来改变时间间隔。同时,定时器会持续发射(也就是循环)直到被杀死,而且在事件派发器接口中也没有 "单次发射(single shot)" 的概念。高层的API必须在第一次发射后通过反注册来模拟一个单次发射。
定时器 id 分配算法
定时器ID的分配算法是无锁的而且可从
Brad的博客
中学习。
一个重要的事实是,定时器ID必须是唯一的(甚至在跨线程的情况下)。这是因为当一个QObject从一个线程移动到另一个时,它的定时器也会随它一起移动(即信号和事件现在要通过新线程的事件循环进行传递)。移动定时器是一个简单从旧线程的派发器注销计时器并在在新线程的派发器中注册的问题。如果定时器的ID是不是唯一的,定时器ID可能会与新线程中现有的定时器发生冲突。如果新的定时器ID在QObject::moveToThread时被重新生成,那么应用程序需要被通知id信息发生更改,而且这对程序员来说是一个麻烦。
Unix (不用 glib)
在Unix中,QEventDispatcherUNIX实现了事件派发。它按照到期时间维护着一个计时器的列表。当计时器注册时,它的到期时间是通过在单调时钟(clock_gettime)上加上超时值来计算出来的。在不支持单调时钟的系统中函数gettimeofday会被使用。然后按照它的到期时间间隔定时器被插入排序后的列表中。
派发器使用 select() 来等待所有的 fds(X 连接,套接字)事件。当进入 processEvents() 时它首先为所有注册的计时器更新到期时间间隔。然后将定时器列表中第一项的时间间隔作为超时值提供给系统调用 seletc()。从 select 返回后,派发器通过给关联的 QObject 发射 QEvent::Timer 来激活到期的定时器。
注意,Qt 未使用 POSIX 定时器的API - timerfd_create(为什么?可移植性?)。
Unix (使用 glib)
Qt 可以使用 glib 事件循环(默认)来编译,它有助于与Gtk 更好地集成。
类似于 QEventDispatcherUNIX,QEventDispatcherGlib 管理一个按到期时间排序的定时器的列表。它为定时器创建新的 GSource。该自定义 GSource 的准备、检查与派发函数在定时器列表上以显而易见的方式的工作(fix me)。
Windows
Qt 创建一个带回调函数的隐藏窗口来处理事件。当定时器被注册时,QEventDispatcherWin 将基于定时器的时间间隔创建的 Windows原生的定时器。
-
如果时间间隔大于20毫秒,它使用 SetTimer(伴随着QAbstractEventDispatcher::registerTimer生成的id)。SetTimer发送WM_TIMER消息到回调函数中并作为QEvent::Timer发送到QObject。
- 如果时间间隔小于20毫秒,Qt将尝试通过timeSetEvent使用多媒体(也就是fast)定时器。timeSetEvent 接受一个回调函数,到期时它会被调用并在一个独立的线程中被调用。在Qt中,定时器在QObject相同的线程中发射。因此 timeSetEvent 的回调函数 post 一个消息到派发器,派发器将获得该消息并作为 QEvent::Timer 发送。
-
如果时间间隔为0,注册时派发器将post一个特殊的消息(QEvent::ZeroTimerEvent)到它自身。派发器通过发送QEvent::Timer到相应的QObject并post一个ZeroTimerEven到它自身来处理该事件。
API 实现
QObject::startTimer 使用事件派发器注册一个新的定时器。这大约等价于,
QAbstractEventDispatcher::instance(object->thread())->registerTimer(interval, object).
QTimer 是一个 QObject。他所做的就是 QObject::startTimer()。它在自己的timerEvent()中发射 activated() 信号。
QBasicTimer
QTimer由于本身是一个QObject所以显得比较笨拙,而且Qt代码一般都尽量避免使用它。与此同时,使用 QObject::startTimer() API 又比较容易出错。
- 当停止定时器时,在QObject::killTimer(m_timerId)后你必须使定时器id变无效(比如说-1)。这是需要的,因为m_timerId的正值在你的代码中指示该定时器处于激活状态。
- 要重启一个定时器,你需要牢记如果m_timerId不为-1的话要杀死(kill)掉老的定时器。
QBasicTimer 解决了上面这个问题,它只是整数 "timer id" 的一个华而不实的接口。QBasicTimer::start(int msec, QObject *) 将杀死任何可能存在的定时器并使用 object->startTimer(msec) 创建一个新的。QBasicTimer::stop() 将杀死该定时器并使得id无效。QBasicTimer::timerId() 提供定时器 id。
扩展阅读
分享到:
相关推荐
C语言02-Timer0-Timer1-Timer2-Timer3-Timer4测试程序(STC32G-DEMO-CODE-220311kw)C语言02-Timer0-Timer1-Timer2-Timer3-Timer4测试程序(STC32G-DEMO-CODE-220311kw)C语言02-Timer0-Timer1-Timer2-Timer3-Timer4...
最新单片机仿真 TIMER0与TIMER1控制条形LED最新单片机仿真 TIMER0与TIMER1控制条形LED最新单片机仿真 TIMER0与TIMER1控制条形LED最新单片机仿真 TIMER0与TIMER1控制条形LED最新单片机仿真 TIMER0与TIMER1控制条形LED...
单片机C语言程序设计 TIMER0与TIMER1控制条形LED(有源码)单片机C语言程序设计 TIMER0与TIMER1控制条形LED(有源码)单片机C语言程序设计 TIMER0与TIMER1控制条形LED(有源码)单片机C语言程序设计 TIMER0与TIMER1控制...
本文实例讲述了C#中Forms.Timer、Timers.Timer、Threading.Timer的用法分析,分享给大家供大家参考。具体分析如下: 在.NET Framework里面提供了三种Timer ① System.Windows.Forms.Timer ② System.Timers.Timer ③...
单片机C语言程序设计31 TIMER0与TIMER1控制条形LED(基于8051+Proteus仿真)单片机C语言程序设计31 TIMER0与TIMER1控制条形LED(基于8051+Proteus仿真)单片机C语言程序设计31 TIMER0与TIMER1控制条形LED(基于8051+...
基于Verilog的timer计时器,start开始,到达设置计时点时输出一个高电平up信号
本文首先设计一个单次定时器Timer+TimerTask,然后再次基础上设计一个循环定时器。
学习32位单片机的基础例程,主要针对初学者对TIMER的编程学习
单片机C语言程序设计TIMER0控制流水灯(基于8051+Proteus仿真)单片机C语言程序设计TIMER0控制流水灯(基于8051+Proteus仿真)单片机C语言程序设计TIMER0控制流水灯(基于8051+Proteus仿真)单片机C语言程序设计...
addintimer高级版是一个万能定时器,非常好用,强烈推荐,可以当时间定时器来用,还有整点报时,定时录音等功能
加Timer控件 timer1 编写其Tick事件为 private void timer1_Tick(object sender, EventArgs e) { this.toolStripStatusLabel3.Text = "系统当前时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"); } ...
1.Timer Mechanism 2.Hardware Timer 3.Timer in OS 4.Timer in APP 5.Timer in Qemu
c8051f340 TIMER0定时器中断,通过定时器达到所需的功能
51单片机Proteus仿真实例 TIMER0控制流水灯51单片机Proteus仿真实例 TIMER0控制流水灯51单片机Proteus仿真实例 TIMER0控制流水灯51单片机Proteus仿真实例 TIMER0控制流水灯51单片机Proteus仿真实例 TIMER0控制流水灯...
java定时任务调度Timer简单示例代码 ,
STC8a8k所有定时器测试程序
单片机C语言程序设计27 TIMER0控制单只LED闪烁(基于8051+Proteus仿真)单片机C语言程序设计27 TIMER0控制单只LED闪烁(基于8051+Proteus仿真)单片机C语言程序设计27 TIMER0控制单只LED闪烁(基于8051+Proteus仿真...
ajax_timer ajax_timer ajax_timer ajax_timer
AddinTimer是手机上的Task Scheduler,可以自动控制各种不同功能,同时具有多种不同的定时方式,几乎可以满足所有场景的定时要求,并且这些定时器可以分类管理,使你摆脱日常设置繁琐的烦恼,AddinTimer简单你的生活...
GD32F330 Timer定时器中断功能使用