用Windows API编写一个精确定时的循环

来源:互联网 发布:边锋网络清墩下载 编辑:程序博客网 时间:2024/05/17 00:02

 

这几天跟网友讨论一个程序优化问题时,遇到一个需要延时的循环,结合我所知道的知识,并突发灵感,想到了用Windows系统提供的定时器API写一个精确延时循环的方法。

问题原本是这个样子的,就是说,在一个循环中执行一些操作后,需要用Sleep这个函数,延迟一定时间后,在执行下一轮循环,伪代码如下:

for(int i = 0; i < MaxCount;i++)

{

     dosomething();

     Sleep(SleepTime);

}

基于Sleep延时的不稳定,不精确性,这个循环延时,效果非常的糟糕,我想到的改进办法就是使用Waitable Timer内核对象来精确延时改造循环,因为这个内核对象可以精确定时到1ms这个量级,当然改造后,可以精确定时到100纳秒这么水平,首先我们来看看,如何改造成精确1ms一次的循环:

    HANDLE hTimer = NULL;

    LARGE_INTEGER liDueTime;

    liDueTime.QuadPart = -1;

    hTimer = CreateWaitableTimer(NULL, FALSE, NULL);

    if (NULL == hTimer)

    {

        //错误处理

    }

    LONG lCycle = 1;  //1毫秒触发一次

    if (!SetWaitableTimer(hTimer, &liDueTime, lCycle, NULL, NULL, 0))

    {

        //错误处理

    }

    int i = MaxCount;

    while( (i > 0) && WaitForSingleObject(hTimer, INFINITE) != WAIT_OBJECT_0)

    {//循环

        dosomething();

        --i;

    }

像上面这样我们就得到一个1ms执行一次的循环,当然看似程序有点复杂,但是这个复杂性只是一种表面的,实际上我们得到了一个1毫秒循环一次的循环,当然这个需要你确定你的dosomething这样的循环体函数的执行时间是1毫秒以内的,否则这个延时就没有意义了。

接着我们再来考虑如何得到一个精确到100纳秒一次的循环,这个就需要一点技巧了,首先我们不能再创建自动重置的计时器对象,而要改为手工,因为100纳秒这个精度只有再次调用SetWaitableTimer才能得到,所以必须要手工重置,每次都调用一下这个函数,重置等待100纳秒,不啰嗦了,上代码:

    HANDLE hTimer = NULL;

    LARGE_INTEGER liDueTime;

    liDueTime.QuadPart = 1;

    hTimer = CreateWaitableTimer(NULL, TRUE, NULL);

    if (NULL == hTimer)

    {

        //出错处理

    }

   

    if (!SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, 0))

    {

        //出错处理

    }

    int i = MaxCount;

    while( (i > 0) && WaitForSingleObject(hTimer, INFINITE) != WAIT_OBJECT_0)

    {

        dosomething();

        if (!SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, 0))

        {

            //出错处理

            break;

        }

        --i;

    }

这段代码和前段代码,有些细节方面的差异,仔细看代码就知道了,不再赘述。而需要提醒注意的就是,这个实际上是使每次dosomething都间隔100纳秒调用,与前一个循环逻辑上是不同的,所以这个要特别注意,当然通过把SetWaitableTimer函数调整到dosomething之前,就可以得到与前一个循环逻辑一致的循环了,这不是什么难事,当然这个就需要你的函数一定要在100纳秒以内干完活,否则,延时就没有什么意义了。

最后,还是提醒下吧,liDueTime本质是个64位的变量,在作为SetWaitableTimer函数的参数时,它的最小时间单位就是100纳秒,这样你赋值1就表示1*100纳秒,2就表示200纳秒,以此类推,但是你没法指定小数来得到1.5*100纳秒这样的数,这就是100纳秒为单位的确切含义,100纳秒就成了不可逾越的鸿沟。当然对于一般目的的程序来说,这足够了。同样的那个lCycle参数的含义也是一样的,只是单位是1毫秒最小,无法再小了,同时第二个定时循环中没有用这个参数,在这种情况下用它也是没有什么意义的。

至此精确延时循环的编写方法就介绍完了,他可以用于不用消息循环,而需要周期性操作的场合,比如周期性的发送一个数据包,周期性的轮询一个设备的状态等。