Multimedia Timers
到目前为止,所知道的精度最高的定时器就是多媒体定时器了,大多数系统应该都可以达到1ms的精度,而其他SetTimer,CreateWaitableTimer,Sleep,GetTickCount等都只能达到15,16ms。
因为以前对GetTickCount不了解,所以今天早上在验证timeSetEvent的时候,老是发现1ms的定时器每次取的时间都不是相差1ms,或者是相同,或者相差15,16毫秒,这次终于明白了。(用timeGetTime才可能达到1ms精度)
在使用多媒体定时器之前,我们必须调用timeBeginPeriod函数设置精度,如果不设置的话,多媒体定时器就和其他的定时器一样,精度在15,16ms左右,也就是说timeGetTime,timeSetEvent都不准了。
所有的定时器对应的回调函数都是运行在一个线程中。
注意,多媒体定时器是每隔多长时间触发一次,假如回调函数没把时间花完的话,很好理解,也很准!假如回调函数花的时间超过了定时器间隔的话,这次回调函数执行完之后,马上就会触发下一次。(不是之前理解的回调执行完之后再隔多长时间后才触发)
精度的意思就是偏离准确值的大小,比如当前时间是100,如果精度是5ms,那么返回的时间可能在95~105之间。Forexample, if you specify a resolution of 5 and an event delay of 100, the timerservices notify the callback function after an interval ranging from 95 to 105milliseconds.
一个简单的使用例子
#include <windows.h>
#include <stdio.h>
#include <Mmsystem.h>
#pragma comment(lib, "Winmm.lib")
void CALLBACK OneShotTimer(UINT wTimerID, UINT msg,
DWORD dwUser, DWORD dw1, DWORD dw2)
{
FILE* pf = (FILE*)dwUser;
char buf[256];
sprintf_s(buf, "timeGetTime = %u\n", timeGetTime());
fputs(buf, pf);
fflush(pf);
}
#define TARGET_RESOLUTION 1 //1-millisecond target resolution
int main()
{
FILE* pf = fopen("D:\\a.txt","w");
TIMECAPS tc;
UINT wTimerRes;
if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR)
{
// Error; application can't continue.
}
wTimerRes = min(max(tc.wPeriodMin, TARGET_RESOLUTION),tc.wPeriodMax);
timeBeginPeriod(wTimerRes);
UINT nTimerID = timeSetEvent(1, //delay
wTimerRes, //resolution (global variable)
OneShotTimer, //callback function
(DWORD_PTR)pf, //user data
TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
getchar();
timeKillEvent(nTimerID);
timeEndPeriod(wTimerRes);
return 0;
}
没搞懂的问题:
1、当调用timeBeginPeriod设置精度后,好像timeSetEvent中的第二个参数就没效果了,我试一下,用timeBeginPeriod设置精度为1ms,在timeSetEvent中传递的精度为5ms,但是系统还是每隔1ms触发。
假如不调用timeBeginPeriod,直接调用timeSetEvent传递一个精度,此时会有效吗?因为没用调用timeBeginPeriod,导致没有取精确时间的函数来验证?
2.You must match each call totimeBeginPeriod with a call to timeEndPeriod, specifying the same minimumresolution in both calls. An application can make multiple timeBeginPeriodcalls, as long as each call is matched with a call to timeEndPeriod.
假如我们没有按规定做会出现什么情况?
答:例
ret = timeBeginPeriod(wTimerRes);
ret = timeEndPeriod(wTimerRes); // 你得先调用它
ret = timeBeginPeriod(5);
上面这段话的意思是,当你要设置新的精度的时候,如果之前你设置过,你得调用timeEndPeriod后再设置。否则,高精度的会覆盖掉低精度的,但低精度的不会覆盖高精度的。
设置的这个精度,实际上是一个全局的东西,一改变会影响所有东西,包括已存在的定时器。
注意:
一、多媒体定时器线程的优先级是15级,如果太忙,可能会造成其他线程根本得不到处理的机会。
二、timeKillEvent和回调函数是异步的,即如果回调正在执行,timeKillEvent不会等待回调执行完才返回,而是直接返回,sip呼叫器关闭时发生崩溃就是这个原因引起的。所以,在结束多媒体定时器的时候要慎重!