定时器时间设置的很小,在杀死定时器的时候,定时器很难马上结束。这是不是必然的?是不是与“让一架高速运动的战机和低速行走的人停止,需要的时间多少是不一样的原理”是一样的?
现在测试部给我提了这个问题,限定时间不要太小是可以解决这个问题,但是现在软件的需求允许时间很小的情况。请问这是问题么?能改么?我觉得这不是问题,既然允许了周期执行时间很小10毫秒,就必然会有这个问题。请大侠指点。

解决方案 »

  1.   

    计时器并不精确。有两个原因:  原因一:Windows计时器是硬件和ROM BIOS架构下之计时器一种相对简单的扩充。回到Windows以前的MS-DOS程序写作环境下,应用程式能够通过拦截者称为timer tick的BIOS中断来实现时钟或计时器。一些为MS-DOS编写的程序自己拦截这个硬件中断以实现时钟和计时器。这些中断每54.915毫秒产生一次,或者大约每秒18.2次。这是原始的IBM PC的微处理器频率值4.772720 MHz被218所除而得出的结果。在Windows 98中,计时器与其下的PC计时器一样具有55毫秒的解析度。在Microsoft Windows NT中,计时器的解析度为10毫秒。Windows应用程式不能以高于这些解析度的频率(在Windows 98下,每秒18.2次,在Windows NT下,每秒大约100次)接收WM_TIMER消息。在SetTimer中指定的时间间隔总是截尾后tick数的整数倍。例如,1000毫秒的间隔除以54.925毫秒,得到18.207个tick,截尾后是18个tick,它实际上是989毫秒。对每个小于55毫秒的间隔,每个tick都会产生一个WM_TIMER消息。 
      可见,计时器并不能严格按照指定的时间间隔发送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的数值增大。而且定时器实时控制功能完成后要尽快释放。 
      

  2.   

    你定时器执行的函数,每激活一次需要多长时间结束?
    有没有可能是你定时器执行的函数正运行到一半,来KillTimer请求了,这样Timer就不能被立刻删除。是的话,看看是不是可以在定时器执行函数中,加个变量观察一下,如果要kill了,函数立刻返回。
      

  3.   

    误差应该忽略了,你又不是又来计时,差也不会差几毫秒,相反你应最关心的是运行到KillTimer时的时间
    如果路上有循环之类的会担误一些时间,按理来说kill掉那个id那就不工作了,不会有太大的延迟,不像高速火车一样
      

  4.   

    我现在的问题就是周期执行的时间很小的时候,Kill掉那个ID,要过很长时间那线程(定时器相当与一个线程)才结束。
      

  5.   

    不可能,你就是没有Kill掉那个ID,我刚测试了一下,也是SetTimer(1,10,0)
    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之类的看会不会影响
      

  6.   

    设置一个 标示变量。
    而且timer是个低优先级的东西,确实不是那么准。
      

  7.   

    楼主可以说是用QueryPerformanceFrequency和QueryPerformanceCounter制作自己的定时器,定时时间和停止都可以精确到10纳秒的。下面是我测试代码时用的sleep函数,缺点是使用时候需要单独开个线程。bool  MySleep(DWORD dwMicrosecond )
    {
    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;
    }
      

  8.   


    我觉得有道理,killTimer不会在本次函数没执行完就终止,只会到下次不再执行。
      

  9.   

    你那OnTimer()函数做的处理太简单了。如果该函数执行的是对服务器100个点进行写操作。你试着在里面做个复杂for循环试试。
      

  10.   

    你使用定时器,10ms,你想一下.每10ms你的消息加入队列中,killTimer也是要加入队列中.killTimer消息的优先级不会比OnTimer高.所以你会感觉好几次才停止.如把你设置setTimer500ms,就不会出现这种显象.一般OnTimer只做一些不重要的事.我把处理会开一个线程.
      

  11.   

    你的定时器执行了耗时的操作,导致消息阻塞
    Kill命令得不到响应改用多线程吧
      

  12.   

    试一下 CreateTimerQueueTimer 和 DeleteTimerQueueTimer
      

  13.   

    timer响不响应,跟你的timer的复杂度,定时长短都有关。楼上的,你求人进你的群还这么多要求。