想要执行完一段语句后
等待一段时间执行下段语句
希望精确度越高越好(误差<=10ms)
我原来用Sleep()发现误差很大另外说明一下:
我是在一个OnTimer函数中进行延时的

解决方案 »

  1.   

    使用GetTickCount试试
    DWORD GetTickCount(VOID);
    The GetTickCount function retrieves the number of milliseconds that have elapsed since the system was started. It is limited to the resolution of the system timer. To obtain the system timer resolution, use the GetSystemTimeAdjustment function. 
      

  2.   

    gettickcount();
    别用timer,因为WM_TIMER的优先级可不高,所以精确度难保证。

    int n=gettickcount();
    while(gettickcount()-n<yourInterval)
    {
      //处理消息循环,就是说在等待时也能响应其它消息,如果你不想这样,直接留成空循环。我记不得怎么写的了,象是如下,请高手指正:
      MSG msg;
      if(GetMessage(msg,...))
      {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
      }
    }
      

  3.   

    三种Windows中的定时或计时方法
    大连理工大学
    孙鹤泉
      随着软硬件的飞速发展,计算机技术已经广泛地应用到自动化控制领域,为了实现实时控制,控制程序应该能够精确地完成定时和计时。Visual C++提供了很多关于时间操作的函数,下面根据它们精度的不同,分别进行说明。   任何Visual C++的程序员都会利用Windows的WM_TIMER消息映射来进行简单的时间控制:1、调用函数SetTimer()设置定时间隔,如SetTimer(0,200,NULL)即为设置200毫秒的时间间隔;2、在应用程序中增加定时响应函数OnTimer(),并在该函数中添加响应的处理语句,用来完成定时时间到时的操作。这种定时方法是非常简单的,但其定时功能如同Sleep()函数的延时功能一样,精度非常低,只可以用来实现诸如位图的动态显示等对定时精度要求不高的情况,但在精度要求较高的条件下,这种方法应避免采用。   在要求误差不大于1毫秒的情况下,可以采用GetTickCount()函数(如果读者仍然使用Windows3.1,可以使用GetCurrentTime()函数),该函数的返回值是DWORD型,表示以毫秒为单位的计算机启动后经历的时间间隔。使用下面的编程语句,可以实现50毫秒的精确定时,其误差小于1毫秒。以下语句已经使用在大连理工大学海岸和近海工程国家重点实验室为广东省水利水电科学研究所研制开发的液压伺服多向不规则造波机系统的控制程序中。 DWORD  dwStart, dwStop ;  // 起始值和中止值
    dwStop = GetTickCount();
    while(TRUE)
     {
      dwStart = dwStop ;  // 上一次的中止值变成新的起始值
      // …… 此处添加相应控制语句 ……
      do
       {
        dwStop = GetTickCount() ;
       } while(dwStop - 50 < dwStart) ;
     }
      对于一般的实时控制,使用GetTickCount()函数就可以满足精度要求。但作者在为大连基康公司编写快速计数程序时,发现使用GetTickCount()函数对计数结果产生很大影响。为了进一步提高计时精度,作者使用了QueryPerformanceFrequency()函数和QueryPerformanceCounter()函数。这两个函数是Visual C++提供的仅供Windows 95及其后续版本使用的高精度时间函数,并要求计算机从硬件上支持高精度计时器。QueryPerformanceFrequency()函数和QueryPerformanceCounter()函数的原型为: 
    BOOL  QueryPerformanceFrequency(LARGE_INTEGER  *lpFrequency) ;
    BOOL  QueryPerformanceCounter(LARGE_INTEGER  *lpCount) ;
      数据类型LARGE_INTEGER既可以是一个作为8字节长的整型数,也可以作为两个4字节长的整型数的联合结构,其具体用法根据编译器是否支持64位而定。该类型的定义如下: 
    typedef  union  _LARGE_INTEGER
     {
      struct
       {
        DWORD  LowPart ;  // 4字节整型数
        LONG    HighPart ;  // 4字节整型数
       };
      LONGLONG    QuadPart ;  // 8字节整型数
     }  LARGE_INTEGER ;
      在进行计时之前,应该先调用QueryPerformanceFrequency()函数获得机器内部计时器的时钟频率。作者在主频为266、300、333的三种PentiumⅡ机器上使用该函数,得到的时钟频率都是1193180Hz。接着,作者在需要严格计时的事件发生之前和发生之后分别调用QueryPerformanceCounter()函数,利用两次获得的计数之差和时钟频率,就可以计算出事件经历的精确时间。下面的程序是用来测试函数Sleep(100)的精确持续时间。 
    LARGE_INTEGER  litmp ;
    LONGLONG  QPart1,QPart2 ;
    double  dfMinus, dfFreq, dfTim ;QueryPerformanceFrequency(&litmp) ; 
     // 获得计数器的时钟频率
    dfFreq = (double)litmp.QuadPart ;QueryPerformanceCounter(&litmp) ; 
     // 获得初始值
    QPart1 = litmp.QuadPart ;Sleep(100) ;QueryPerformanceCounter(&litmp) ; 
     // 获得中止值
    QPart2 = litmp.QuadPart ;dfMinus = (double)(QPart2 - QPart1) ;
    dfTim = dfMinus / dfFreq ;  
    // 获得对应的时间值
      执行上面程序,得到的结果为dfTim=0.097143767076216(秒),细心的读者会发现,每次执行的结果都不一样,存在一定的差别,这是由于Sleep()自身的误差所致。 
      本文介绍了三种定时或计时的实现方法,读者可以根据自己的实际情况进行选择,以达到程序的定时和计时功能。以上程序均使用Visual C++5.0和6.0在Windows98下调试通过。
      

  4.   

    使用CPU时间戳进行高精度计时对关注性能的程序开发人员而言,一个好的计时部件既是益友,也是良师。计时器既可以作为程序组件帮助程序员精确的控制程序进程,又是一件有力的调试武器,在有经验的程序员手里可以尽快的确定程序的性能瓶颈,或者对不同的算法作出有说服力的性能比较。
    在Windows平台下,常用的计时器有两种,一种是timeGetTime多媒体计时器,它可以提供毫秒级的计时。但这个精度对很多应用场合而言还是太粗糙了。另一种是QueryPerformanceCount计数器,随系统的不同可以提供微秒级的计数。对于实时图形处理、多媒体数据流处理、或者实时系统构造的程序员,善用QueryPerformanceCount/QueryPerformanceFrequency是一项基本功。
    本文要介绍的,是另一种直接利用Pentium CPU内部时间戳进行计时的高精度计时手段。以下讨论主要得益于《Windows图形编程》一书,第15页-17页,有兴趣的读者可以直接参考该书。关于RDTSC指令的详细讨论,可以参考Intel产品手册。本文仅仅作抛砖之用。在Intel Pentium以上级别的CPU中,有一个称为“时间戳(Time Stamp)”的部件,它以64位无符号整型数的格式,记录了自CPU上电以来所经过的时钟周期数。由于目前的CPU主频都非常高,因此这个部件可以达到纳秒级的计时精度。这个精确性是上述两种方法所无法比拟的。
    在Pentium以上的CPU中,提供了一条机器指令RDTSC(Read Time Stamp Counter)来读取这个时间戳的数字,并将其保存在EDX:EAX寄存器对中。由于EDX:EAX寄存器对恰好是Win32平台下C++语言保存函数返回值的寄存器,所以我们可以把这条指令看成是一个普通的函数调用。像这样:inline unsigned __int64 GetCycleCount()
    {
     __asm RDTSC
    }但是不行,因为RDTSC不被C++的内嵌汇编器直接支持,所以我们要用_emit伪指令直接嵌入该指令的机器码形式0X0F、0X31,如下:inline unsigned __int64 GetCycleCount()
    {
     __asm _emit 0x0F
     __asm _emit 0x31
    }以后在需要计数器的场合,可以像使用普通的Win32 API一样,调用两次GetCycleCount函数,比较两个返回值的差,像这样:unsigned long t;
    t = (unsigned long)GetCycleCount();
    //Do Something time-intensive ...
    t -= (unsigned long)GetCycleCount();《Windows图形编程》第15页编写了一个类,把这个计数器封装起来。有兴趣的读者可以去参考那个类的代码。作者为了更精确的定时,做了一点小小的改进,把执行RDTSC指令的时间,通过连续两次调用GetCycleCount函数计算出来并保存了起来,以后每次计时结束后,都从实际得到的计数中减掉这一小段时间,以得到更准确的计时数字。但我个人觉得这一点点改进意义不大。在我的机器上实测,这条指令大概花掉了几十到100多个周期,在Celeron 800MHz的机器上,这不过是十分之一微秒的时间。对大多数应用来说,这点时间完全可以忽略不计;而对那些确实要精确到纳秒数量级的应用来说,这个补偿也过于粗糙了。这个方法的优点是:
    1.高精度。可以直接达到纳秒级的计时精度(在1GHz的CPU上每个时钟周期就是一纳秒),这是其他计时方法所难以企及的。
    2.成本低。timeGetTime 函数需要链接多媒体库winmm.lib,QueryPerformance* 函数根据MSDN的说明,需要硬件的支持(虽然我还没有见过不支持的机器)和KERNEL库的支持,所以二者都只能在Windows平台下使用(关于DOS平台下的高精度计时问题,可以参考《图形程序开发人员指南》,里面有关于控制定时器8253的详细说明)。但RDTSC指令是一条CPU指令,凡是i386平台下Pentium以上的机器均支持,甚至没有平台的限制(我相信i386版本UNIX和Linux下这个方法同样适用,但没有条件试验),而且函数调用的开销是最小的。
    3.具有和CPU主频直接对应的速率关系。一个计数相当于1/(CPU主频Hz数)秒,这样只要知道了CPU的主频,可以直接计算出时间。这和QueryPerformanceCount不同,后者需要通过QueryPerformanceFrequency获取当前计数器每秒的计数次数才能换算成时间。这个方法的缺点是:
    1.现有的C/C++编译器多数不直接支持使用RDTSC指令,需要用直接嵌入机器码的方式编程,比较麻烦。
    2.数据抖动比较厉害。其实对任何计量手段而言,精度和稳定性永远是一对矛盾。如果用低精度的timeGetTime来计时,基本上每次计时的结果都是相同的;而RDTSC指令每次结果都不一样,经常有几百甚至上千的差距。这是这种方法高精度本身固有的矛盾。关于这个方法计时的最大长度,我们可以简单的用下列公式计算:自CPU上电以来的秒数 = RDTSC读出的周期数 / CPU主频速率(Hz)64位无符号整数所能表达的最大数字是1.8×10^19,在我的Celeron 800上可以计时大约700年(书中说可以在200MHz的Pentium上计时117年,这个数字不知道是怎么得出来的,与我的计算有出入)。无论如何,我们大可不必关心溢出的问题。下面是几个小例子,简要比较了三种计时方法的用法与精度
    //Timer1.cpp 使用了RDTSC指令的Timer类//KTimer类的定义可以参见《Windows图形编程》P15
    //编译行:CL Timer1.cpp /link USER32.lib
    #include <stdio.h>
    #include "KTimer.h"
    main()
    {
     unsigned t;
     KTimer timer;
     timer.Start();
     Sleep(1000);
     t = timer.Stop();
     printf("Lasting Time: %d\n",t);
    }//Timer2.cpp 使用了timeGetTime函数
    //需包含<mmsys.h>,但由于Windows头文件错综复杂的关系
    //简单包含<windows.h>比较偷懒:)
    //编译行:CL timer2.cpp /link winmm.lib 
    #include <windows.h>
    #include <stdio.h>main()
    {
     DWORD t1, t2;
     t1 = timeGetTime();
     Sleep(1000);
     t2 = timeGetTime();
     printf("Begin Time: %u\n", t1);
     printf("End Time: %u\n", t2);
     printf("Lasting Time: %u\n",(t2-t1));
    }//Timer3.cpp 使用了QueryPerformanceCounter函数
    //编译行:CL timer3.cpp /link KERNEl32.lib
    #include <windows.h>
    #include <stdio.h>main()
    {
     LARGE_INTEGER t1, t2, tc;
     QueryPerformanceFrequency(&tc);
     printf("Frequency: %u\n", tc.QuadPart);
     QueryPerformanceCounter(&t1);
     Sleep(1000);
     QueryPerformanceCounter(&t2);
     printf("Begin Time: %u\n", t1.QuadPart);
     printf("End Time: %u\n", t2.QuadPart);
     printf("Lasting Time: %u\n",( t2.QuadPart- t1.QuadPart));
    }////////////////////////////////////////////////
    //以上三个示例程序都是测试1秒钟休眠所耗费的时间
    file://测/试环境:Celeron 800MHz / 256M SDRAM  
    //          Windows 2000 Professional SP2
    //          Microsoft Visual C++ 6.0 SP5
    ////////////////////////////////////////////////
    以下是Timer1的运行结果,使用的是高精度的RDTSC指令
    Lasting Time: 804586872以下是Timer2的运行结果,使用的是最粗糙的timeGetTime API
    Begin Time: 20254254
    End Time: 20255255
    Lasting Time: 1001以下是Timer3的运行结果,使用的是QueryPerformanceCount API
    Frequency: 3579545
    Begin Time: 3804729124
    End Time: 3808298836
    Lasting Time: 3569712古人说,触类旁通。从一本介绍图形编程的书上得到一个如此有用的实时处理知识,我感到非常高兴。有美不敢自专,希望大家和我一样喜欢这个轻便有效的计时器。参考资料:
    [YUAN 2002]Feng Yuan 著,英宇工作室 译,Windows图形编程,机械工业出版社,2002.4.,P15-17
      

  5.   

    http://www.5xsoft.com/data/200112/2908075501.htm