您的位置:首页 > 其它

Windows下高精度定时器讨论

2017-12-11 13:26 501 查看
获得高精度时间点是可能的,但高精度时间段是比较难的,精度越高受到各方面的影响越大

只有QueryPerformanceCounter能突破1ms,内部就是一条汇编语句直接读cpu晶振读数,

但容易受到线程排队和消息队列延迟带来的影响,不稳定

QueryPerformanceCounter

缺点是精度不够高,优点是产生的间隔能突破1ms的限制,可以达到更小的间隔,理论上事件产生的频率可以和系统定时器的频率一样

如果为0.001则为1ms产生一次,理论上如果Interval为1,则以最大的频率产生事件。即可以用Windows产生很高频率的事件,但是由于线程的调用是要有时间的,有的时候可能会造成这个线程一直没有得到执行,从而造成有一段时间没有进行计数,这段时间的定时事件就没有产生了,如果定时的频率越高,丢失的可能性就越大。但如果用它来产生高频随时间变化的随机信号还是很有价值的。这在实时仿真中尤其如此。
此外cpu占用也很高,及时相应cpu就一定很忙

private void CountTime(long dwUs)

        {

            if (dwUs < 0) return;

            long ctr1 = 0, ctr2 = 0;

            if (freq == 0) QueryPerformanceFrequency(ref freq);

            if (QueryPerformanceCounter(ref ctr1) != 0)    // Begin timing.

            {

                do

                {

                    QueryPerformanceCounter(ref ctr2);    // Finish timing.

                } while (((ctr2 - ctr1) * 1.0 * 1000000 / freq) < dwUs);

            }

            else

            {

                Thread.Sleep(Convert.ToInt32(dwUs / 1000));

            }

        }

        [DllImport("kernel32.dll")]

        extern static short QueryPerformanceFrequency(ref long x);

        [DllImport("kernel32.dll")]

        extern static short QueryPerformanceCounter(ref long x);

Windows下要实现稳定的1ms定时是不可能的,Windows本来就不是实时操作系统,当初的设计就是不用来高精度定时,CreateWaitableTimer,SetWaitableTimer 可以精确到100纳秒,但是波动性仍然很大.另外多媒体定时器也可以实现1ms的定时,不过最多只能开16个定时器,而且实际时间波动也不小

我们知道, 在linux上, sleep函数的单位是s, 那怎么进行微妙级别的定时呢? 用select函数即可。 但是, 在Windows上, 强烈不建议将select函数用作定时器(该语句出自大名鼎鼎的Windows Socket这本书), 下面我们来实战一下:

       看程序:

[cpp]
view plain
copy

#include <winsock2.h>  
#include <stdio.h>  
#pragma comment(lib, "ws2_32.lib")  
  
int main()  
{  
    WORD wVersionRequested;  
    WSADATA wsaData;  
    wVersionRequested = MAKEWORD(1, 1);  
    WSAStartup( wVersionRequested, &wsaData );  
  
    SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);  
  
      
    fd_set read_set;  
    struct timeval t;  
    FD_ZERO(&read_set);   
    FD_SET(sockClient, &read_set);   
    t.tv_sec = 3;   
    t.tv_usec = 0;  
  
  
    int ret = select(-1, NULL, NULL, NULL, &t);  
    printf("ret is %d, %d, %d\n", ret, GetLastError(), WSAEINVAL);  
  
  
    closesocket(sockClient);  
    WSACleanup();  
  
    return 0;  
}  

       结果为:ret is -1, 10022, 10022
       为什么会这样呢? Windows Sockets 专家Bob Quinn说过, 在Windows中, 如果select函数的第2, 3, 4个参数为NULL, 那么, select函数会经常返回失败的-1.

       好, 我们来改一下程序:

[cpp]
view plain
copy

#include <winsock2.h>  
#include <stdio.h>  
#pragma comment(lib, "ws2_32.lib")  
  
int main()  
{  
    WORD wVersionRequested;  
    WSADATA wsaData;  
    wVersionRequested = MAKEWORD(1, 1);  
    WSAStartup( wVersionRequested, &wsaData );  
  
    SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);  
  
      
    fd_set read_set;  
    struct timeval t;  
    FD_ZERO(&read_set);   
    FD_SET(sockClient, &read_set);   
    t.tv_sec = 3;   
    t.tv_usec = 0;  
  
  
    int ret = select(-1, &read_set, NULL, NULL, &t); // 改动了  
    printf("ret is %d, %d, %d\n", ret, GetLastError(), WSAEINVAL);  
  
  
    closesocket(sockClient);  
    WSACleanup();  
  
    return 0;  
}  

       程序的结果为:ret is 0, 0, 10022
       而且, 我们确实看到, 起到了3秒定时的作用。 但是, 我们看看, 这个定时明显受制于sockClient啊, 根据之前博文的分析, 如果sockClient上有数据可读, 那么程序会立即返回1, 从而失去了定时的作用。

       综上所述, 在Windows上, 不要用select函数做定时器。 而在linux上, 这么用很常见。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息