C++ TIMER与线程的关系,以及WM_TIMER消息的不准确性
一,SetTimer介绍
SetTimer函数定义如下:
UINT_PTR SetTimer(
HWND hWnd, // 窗口句柄
UINT_PTR nIDEvent, // 定时器ID,多个定时器时,可以通过该ID判断是哪个定时器
UINT nElapse, // 时间间隔,单位为毫秒
TIMERPROC lpTimerFunc // 回调函数
);
第四个参数如果为NULL,那么在第一个参数hwnd的窗口过程中处理WM_TIMER消息来处理定时业务;如果第四个参数不为0,指定为一个函数指针,则直接在该回调函数中处理定时业务。
二,定时器与线程的关系
定时器并不是单独创建了一个线程,它存在于创建该定时器的线程中,共享同一个栈。定时器只是和线程的调度方式不一样:线程开始后,随时可以挂起,让其它线程运行,等待下次调度,以便继续执行,因此可以长期运行。
定时器每启动一次,执行一段代码,不可执行长期。长期运行会造成定时器阻塞。线程是单独调度的,定时器只是打断线程的执行,暂时插入运行定时器的代码。
简单来说,就是线程在执行过程中,如果到了定时器该执行的时间,那么该线程会被被中断,插入定时器执行的完整逻辑,定时器逻辑执行完毕,线程恢复正常态,继续运行。
三,WM_TIMER的不准确性
在Windows系统的消息队列中,不同的消息优先级不同,而WM_TIMER则是优先级最低的哪一类(同样的消息和有WM_PAINT),在GetMessage()时,要等队列里没有其他消息了才返回它。当队列里有其他任何优先级高的消息时,会优先处理其他消息,最后才轮到WM_TIMER,所以WM_TIMER消息一般都要拖延很长时间才能从队列里取出,这就延误了整理的时机。
四,解决方案
微软公司在其多媒体Windows中提供了精确定时器的底层API支持。利用多媒体定时器可以很精确地读出系统的当前时间,并且能在非常精确的时间间隔内完成一个事件、函数或过程的调用。利用多媒体定时器的基本功能,可以通过两种方法实现精确定时。
1)使用timeGetTime()函数,该函数定时精度为ms级,返回从Windows启动开始所经过的时间。由于使用该函数是通过查询的方式进行定时控制的,所以,应该建立定时循环来进行定时事件的控制。
2)使用timeSetEvent()函数,该函数声明如下:
(在定时器使用完毕后, 应及时调用timeKillEvent()将之释放。)
MMRESULT timeSetEvent( UINT uDelay,
UINT uResolution,
LPTIMECALLBACK lpTimeProc,
WORD dwUser,
UINT fuEvent )
uDelay:以毫秒指定事件的周期。
Uresolution:以毫秒指定延时的精度,数值越小定时器事件分辨率越高。缺省值为1ms。
LpTimeProc:指向一个回调函数。
DwUser:存放用户提供的回调数据。
FuEvent:指定定时器事件类型:
TIME_ONESHOT:uDelay毫秒后只产生一次事件
TIME_PERIODIC :每隔uDelay毫秒周期性地产生事件。