如何在 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
)并让相应信号的信号处理程序(即 SIGALRM
或 SIGVTALRM
)在计时器结束后重新启动计时器。然后,您可以进行计算并使用 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 */
我正在尝试用 C 语言创建一个例程,每隔 X 毫秒执行一些代码。
我已经用 C# 完成了,但我需要这个程序在 C 中。
我曾尝试使用 time(0) 或 clock() 来实现它,但这些函数的问题是我可以让系统在执行某些代码后等待一定时间。
我的意图是让代码在时间块 X 中执行。 假设 X 是 25ms 执行一些代码。 之后,如果我还有时间,则无需做任何事情,直到 X = 25
否则我已经用了 30 毫秒来执行我的操作,我将不得不等到下一个时钟滴答来做其他事情。
提前感谢您的任何建议。
我在这个回答中使用的函数是POSIX函数。在 Windows 等非 POSIX 系统上,这些功能可能不可用。
解决此类问题的一般方法是让系统每 25 毫秒或任何您必须等待的持续时间向您发送一个信号。一种方法是设置一个计时器(例如使用 setitimer
)并让相应信号的信号处理程序(即 SIGALRM
或 SIGVTALRM
)在计时器结束后重新启动计时器。然后,您可以进行计算并使用 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 */