定时器时间设置的很小,在杀死定时器的时候,定时器很难马上结束。这是不是必然的?是不是与“让一架高速运动的战机和低速行走的人停止,需要的时间多少是不一样的原理”是一样的?
现在测试部给我提了这个问题,限定时间不要太小是可以解决这个问题,但是现在软件的需求允许时间很小的情况。请问这是问题么?能改么?我觉得这不是问题,既然允许了周期执行时间很小10毫秒,就必然会有这个问题。请大侠指点。
现在测试部给我提了这个问题,限定时间不要太小是可以解决这个问题,但是现在软件的需求允许时间很小的情况。请问这是问题么?能改么?我觉得这不是问题,既然允许了周期执行时间很小10毫秒,就必然会有这个问题。请大侠指点。
可见,计时器并不能严格按照指定的时间间隔发送WM_TIMER消息,它总要相差那么几毫秒。 即使忽略这几个毫秒的差别,计时器仍然不精确。请看原因二:
WM_TIMER消息放在正常的消息队列之中,和其他消息排列在一起,因此,如果在SetTimer中指定间隔为1000毫秒,那么不能保证程序每1000毫秒或者989毫秒就会收到一个WM_TIMER消息。如果其他程序的执行事件超过一秒,在此期间内,您的程式将收不到任何WM_TIMER讯息。事实上, Windows对WM_TIMER消息的处理非常类似于对WM_PAINT消息的处理,这两个消息都是低优先级的,程序只有在消息队列中没有其他消息时才接收它们。
WM_TIMER还在另一方面和WM_PAINT相似:Windows不能持续向消息队列中放入多个WM_TIMER讯息,而是将多余的WM_TIMER消息组合成一个消息。因此,应用程序不会一次收到多个这样的消息,尽管可能在短时间内得到两个WM_TIMER消息。应用程序不能确定这种处理方式所导致的WM_TIMER消息「遗漏」的数目。
可见,WM_TIMER消息并不能及时被应用程序所处理,WM_TIMER在消息队列中的延误可能就不能用毫秒来计算了。 由以上两点,你不能通过在处理WM_TIMER时一秒一秒计数的方法来计时。如果要实现一个时钟程序,可以使用系统的时间函数如GetLocalTime ,而在时钟程序中,计时器的作用是定时调用GetLocalTime获得新的时间并刷新时钟画面,当然这个刷新的间隔要等于或小于1秒。--------------------------
实现微秒级的高精度计时
1. 用GetTickCount()函数:
这个函数的精度约55ms, 即不能测量小于55毫秒的时间间隔. 所有使用缺省系统计时器的函数都只有55ms的精度.这些函数包括:
GetSystemTime, GetTickCount, GetSystemTimeAsFileTime 以及 WM_TIMER 消息.
2. 用多媒体计时器:
可以有1ms的精度, 这些函数包括:
timeBeginPeriod: 设置需要的计时精度,最小1ms
timeGetTime: 获取时间
timeSetEvent: 设置回调方式的计时器
3. 用高精度计时器:
可以有很高的精度, 但具体精度在不的系统上不同. 在高于100MHz的PentiumCPU系统上, 一般计时精度为1us(微秒)
QueryPerformanceFrequency: 获取计时频率
QueryPerformanceCounter: 获取64位计时值
一般的高精度计时器实现不超过1us的精度.
inline DWORD __fastcall usTimeCounter(bool Start)
{
DWORD dwResult;
static LARGE_INTEGER t1;
LARGE_INTEGER t2;
if(Start)
QueryPerformanceCounter(&t1);
else
QueryPerformanceCounter(&t2);
dwResult = t2.QuadPart - t1.QuadPart;
return dwResult;
}
4. RDTSC指令: 指令周期级计时精度.
Pentium及以上IA-32CPU有RDTSC指令, 该指令将CPU从RESET开始的指令周期计数存入EDX:EAX中.
计时频率将达到CPU主频.如1GHz的CPU, 计时精度将达到1ns(纳秒).
虽然Win95下可视化开发工具如VC、Delphi、C++ Builder等都有专用的定时器控件Timer,而且使用很方便,可以实现一定的定时功能,但最小计时精度仅为55ms,且定时器消息在多任务操作系统中的优先级很低,不能得到及时响应,往往不能满足实时控制环境下的应用。不过Microsoft公司在Win32 API函数库中已经为用户提供了一组用于高精度计时的底层函数,如果用户使用得当,计时精度可到1ms。这个计时精度、对于一般的实时系统控制完全可以满足要求。现将由C++ Builder 4.0提供的重新封装后的一组与时间相关的主要接口函数(函数名、参数、功能与Win32 API基本相同)说明如下:
1.DWORD timeGetTime(void)
返回从Windows启动开始经过的毫秒数。最大值为232,约49.71天。
2.MMRESULT timeSetEvent(
UINT uDelay,
UINT uResolution,
LPTIMECALLBACK lpTimeProc,
DWORD dwUser,
UINT fuEvent
)
该函数设置一个定时回调事件,此事件可以是一个一次性事件或周期性事件。事件一旦被激活,便调用指定的回调函数,成功后返回事件的标识符代码,否则返回NULL。参数说明如下:
uDelay:以毫秒指定事件的周期。
UResolution:以毫秒指定延时的精度,数值越小定时器事件分辨率越高。缺省值为1ms。
LpTimeProc:指向一个回调函数。
DwUser:存放用户提供的回调数据。
FuEvent:指定定时器事件类型:
TIME_ONESHOT:uDelay毫秒后只产生一次事件
TIME_PERIODIC :每隔uDelay毫秒周期性地产生事件。
3.MMRESULT timeKillEvent(UINT uTimerID)
该函数取消一个指定的定时器回调事件。uTimerID标识要取消的事件(由timeSetEvent函数返回的标识符)。如果成功则返回TIMERR_NOERROR,如果定时器时间不存在则返回MMSYSERR_INVALPARAM。
void CALLBACK TimeProc(
UINT uID,
UINT uMsg,
DWORD dwUser,
DWORD dw1,
DWORD dw2
);
该函数是一个应用程序定义的回调函数,出现定时器事件时该函数被调用。TimeProc是应用程序定义的函数名的占位符。使用该函数时要注意的是,它只能调用以下有限的几组API函数:PostMessage,timeGetSystemTime, timeGetTime, timeSetEvent,timeKillEvent,midiOutShortMsg, midiOutLongMsg,OutputDebugString。同时也不要使用完成时间很长的API函数,程序尽可能简短。
使用以上一组函数就可以完成毫秒级精度的计时和控制(在C++ Builder中使用时要将头文件mmsystem.h加到程序中)。由于将定时控制精确到几毫秒,定时器事件将占用大量的CPU时间和系统资源,所以在满足控制要求的前提下,应尽量将参数uResolution的数值增大。而且定时器实时控制功能完成后要尽快释放。
有没有可能是你定时器执行的函数正运行到一半,来KillTimer请求了,这样Timer就不能被立刻删除。是的话,看看是不是可以在定时器执行函数中,加个变量观察一下,如果要kill了,函数立刻返回。
如果路上有循环之类的会担误一些时间,按理来说kill掉那个id那就不工作了,不会有太大的延迟,不像高速火车一样
void CTimerView::OnTimer(UINT nIDEvent)
{ count+=1;
Invalidate(false);
CView::OnTimer(nIDEvent);
}
OnDraw(CDC* pDC){
str.Format("%d",count);
pDC->TextOut(10,10,str);
SetTimer(1,10,0);}
我双击Kill后,是瞬间就停住了,你简查你代码有有没有Sleep之类的看会不会影响
而且timer是个低优先级的东西,确实不是那么准。
{
LARGE_INTEGER litmp;
LONGLONG Qpart1,Qpart2;
DOUBLE dfFreq,dfTim;
if(dwMicrosecond==0)
return TRUE;
QueryPerformanceFrequency(&litmp);
dfFreq=(DOUBLE)litmp.QuadPart;
QueryPerformanceCounter(&litmp);
Qpart1=litmp.QuadPart;
while(TRUE)
{
QueryPerformanceCounter(&litmp);
Qpart2=litmp.QuadPart;
dfTim=(double)(Qpart2-Qpart1)/dfFreq;
dfTim=dfTim*1000*1000;
if ((DWORD)dfTim>=dwMicrosecond)
{
break;
} }
return TRUE;
}
我觉得有道理,killTimer不会在本次函数没执行完就终止,只会到下次不再执行。
Kill命令得不到响应改用多线程吧