如何在 C 中实现时钟例程

How to implement a clocking routine in C

我正在尝试用 C 语言创建一个例程,每隔 X 毫秒执行一些代码。

我已经用 C# 完成了,但我需要这个程序在 C 中。

我曾尝试使用 time(0) 或 clock() 来实现它,但这些函数的问题是我可以让系统在执行某些代码后等待一定时间。

我的意图是让代码在时间块 X 中执行。 假设 X 是 25ms 执行一些代码。 之后,如果我还有时间,则无需做任何事情,直到 X = 25

否则我已经用了 30 毫秒来执行我的操作,我将不得不等到下一个时钟滴答来做其他事情。

提前感谢您的任何建议。

我在这个回答中使用的函数是POSIX函数。在 Windows 等非 POSIX 系统上,这些功能可能不可用。

解决此类问题的一般方法是让系统每 25 毫秒或任何您必须等待的持续时间向您发送一个信号。一种方法是设置一个计时器(例如使用 setitimer)并让相应信号的信号处理程序(即 SIGALRMSIGVTALRM)在计时器结束后重新启动计时器。然后,您可以进行计算并使用 pause 系统调用“等待”下一个信号来打断您。

如果您的时间段比这短得多,则信号传输对于您的用例来说可能太慢了。您可以调用 clock 函数来获取您在循环中使用的时间量,并等待它足够接近 25ms 的倍数。这种方法比基于信号的方法延迟更短,但它会使您的处理器忙碌,因此只有在您必须等待的时间非常短时才使用它。这种方法的优点是非常便携,因为 clock 是标准 C 的一部分。

第三种方法是获取当前时间,然后计算您需要等待的时间,直到下一个报价发生,然后调用 nanosleep 或类似的函数来等待该时间。这种方法是不精确的(你可能睡的时间比你想睡的时间多一点),但它不会造成任何漂移,因为你每次找到当前时间时都会与时钟同步。

您必须创建一个无限循环的新线程,该线程将(大约)每 25 毫秒执行一次。

下面是类似于多线程游戏的 windows 代码示例,其中一个线程 运行 以尽可能准确的固定频率运行。延迟基于高精度计时器的原始读数,因此不会随时间漂移。 dwLateStep(参见下面的示例代码)是一种调试辅助工具,如果超过时间步长(代码尝试追赶),它会增加。在 Windows XP 上,Sleep(1) 最多可能需要 2 ms,因此当在下一个时间步之前有超过 2 ms 的延迟时,代码使用 Sleep(1)。

然而,这与您的描述不同,您的代码似乎需要比一个时间步更长的时间。在这种情况下,您 运行 片段中的代码,每个时间步一个片段。假设有 4 个片段,那么您可以使用一个 switch 变量,该变量在从 0 到 3 的每个时间步循环 1,然后回到 0,有 4 种情况。

/* code for a thread to run at fixed frequency */
typedef unsigned long long UI64;        /* unsigned 64 bit int */
#define FREQ    400                     /* frequency */
DWORD    dwLateStep;                    /* late step count */
LARGE_INTEGER liPerfFreq;               /* 64 bit frequency */
LARGE_INTEGER liPerfTemp;               /* used for query */
UI64 uFreq = FREQ;                      /* process frequency */
UI64 uOrig;                             /* original tick */
UI64 uWait;                             /* tick rate / freq */
UI64 uRem = 0;                          /* tick rate % freq */
UI64 uPrev;                             /* previous tick based on original tick */
UI64 uDelta;                            /* current tick - previous */
UI64 u2ms;                              /* 2ms of ticks */
UI64 i;

    /* ... */ /* wait for some event to start thread */
    QueryPerformanceFrequency(&liPerfFreq);
    u2ms = ((UI64)(liPerfFreq.QuadPart)+499) / ((UI64)500);

    timeBeginPeriod(1);                 /* set period to 1ms */
    Sleep(128);                         /* wait for it to stabilize */

    QueryPerformanceCounter((PLARGE_INTEGER)&liPerfTemp);
    uOrig = uPrev = liPerfTemp.QuadPart;

    for(i = 0; i < (uFreq*30); i++){
        /* update uWait and uRem based on uRem */
        uWait = ((UI64)(liPerfFreq.QuadPart) + uRem) / uFreq;
        uRem  = ((UI64)(liPerfFreq.QuadPart) + uRem) % uFreq;
        /* wait for uWait ticks */
        while(1){
            QueryPerformanceCounter((PLARGE_INTEGER)&liPerfTemp);
            uDelta = (UI64)(liPerfTemp.QuadPart - uPrev);
            if(uDelta >= uWait)
                break;
            if((uWait - uDelta) > u2ms)
                Sleep(1);
        }
        if(uDelta >= (uWait*2))
            dwLateStep += 1;
        uPrev += uWait;
        /* fixed frequency code goes here */
        /*  along with some type of break when done */
    }

    timeEndPeriod(1);                   /* restore period */