如何在 C(Linux)中的 while 循环中准确睡眠?

Posted

技术标签:

【中文标题】如何在 C(Linux)中的 while 循环中准确睡眠?【英文标题】:how to sleep accurately in a while loop in C (Linux)? 【发布时间】:2015-06-18 11:39:35 【问题描述】:

在 C 代码(Linux 操作系统)中,我需要在一个 while 循环内准确地休眠 - 比如说,10000 微秒,共 1000 次。我试过usleep、nanosleep、select、pselect和其他一些方法都没有成功。 在大约 50 次中,它的睡眠时间会延长 %100(大约 20000 我们)。 我需要在每次延迟后执行一个动作。因此,每个延迟都必须非常准确。 有没有办法为这种情况做准确的睡眠? 谢谢..

编辑:

#include <stdio.h>
#include <sys/time.h>

int main(int argc, char *argv[]) 
    struct timespec t0, t1;
    long err;
    int i = 0;

    while (i < 1000) 
        clock_gettime(CLOCK_MONOTONIC, &t0);
        usleep(10000);
        clock_gettime(CLOCK_MONOTONIC, &t1);
        err = (long) ((t1.tv_sec - t0.tv_sec) * 1e6
                + (t1.tv_nsec - t0.tv_nsec) / 1000) - 10000;
        printf("i = %d err = %ld\n", i, err);
        i++;
    

    return 0;

结果(日志文件):

i = 0 err = -146i = 1 err = -207i = 2 err = -8i = 3 err = -4i = 4 err = -22 i = 5 err = 31i = 6 err = -45i = 7 err = 9i = 8 err = 61i = 9 err = -71 i = 10 err = -24i = 11 err = 14i = 12 err = -12i = 13 err = -32i = 14 err = -15i = 15 错误 = 42i = 16 错误 = -51i = 17 错误 = -19i = 18 错误 = -12i = 19 错误 = 4i = 20错误 = 12i = 21 错误 = -36i = 22 错误 = -38i = 23 错误 = 18i = 24 错误 = 1i = 25 错误 = - 21i = 26 err = -37i = 27 err = 31i = 28 err = -4i = 29 err = -45i = 30 err = -37 i = 31 错误 = 20i = 32 错误 = -10i = 33 错误 = -5i = 34 错误 = -12i = 35 错误 = -5i = 36 err = -10i = 37 err = -12i = 38 err = -2i = 39 err = 14i = 40 err = -34i = 41 错误 = -10i = 42 错误 = -6i = 43 错误 = 15i = 44 错误 = -34i = 45 错误 = -12 i = 46 错误 = -15i = 47 错误 = -25i = 48 错误 = 11614i = 49 错误 = 2340i = 50 错误 = 589i = 5 1 错误 = 12254i = 52 错误 = -93i = 53 错误 = -19

【问题讨论】:

请让我们看看您使用usleep() 的尝试。这不是一个实时操作系统,但我不敢相信它有这么大的差距。 您确定您指示的 200 毫秒延迟取决于 usleep 功能吗?您是否确认此延迟不依赖于 ISR(中断子例程)或其他类型的系统事件? 我已经编辑了我的问题。对不起,输入错误的数字。由于日志文件,error仍然很高(i = 212时大约50%)。 感谢您的快速回答.. 仍然没有成功.. 【参考方案1】:

睡眠时间较短,然后轮询高频计数器以完成延迟。将所有延迟都基于计数器的原始读数,以防止随时间漂移。例如,previous_count = counter(), wait_count = previous_count + delay_count, delay until counter() - wait_count >= 0, previous_count += delay_count, ... 。如果所需的频率不是 counter() 频率的精确倍数,则使用一个变量来表示延迟的小数部分(我称之为剩余计数)。

由于您提到了 Windows,这里是一个以固定频率运行的线程的示例,并且与 Windows XP 兼容,其中 Sleep(1) 最多可能需要 2 毫秒。 dwLateStep 是一种调试辅助工具,每次循环时间过长时都会增加(如果可能,它会赶上)。延迟基于计数器的原始读数(使用 uWait、uRem、uPrev、uDelta),因此在很长一段时间内没有漂移。这基本上是游戏实现固定频率物理线程的方式。请注意,.net 框架不用于这些类型的游戏,因为它会间歇性地暂停进程以重新打包分页内存。此类游戏会将物理线程优先级设置为高于正常水平,以防止其他线程或进程对其进行干扰。

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 */
        /*  otherwise, this example runs for 30 seconds */
    

    timeEndPeriod(1);                   /* restore period */

【讨论】:

感谢您的回答。我也试过你的方法。抱歉,没有成功。我不是专业程序员,但在我看来,在执行操作时程序会尝试释放内存,这会减慢进程.. 如果进程被暂停以重新打包释放的内存,请尝试提前预分配您需要的内存,并避免进一步释放或分配内存。如果这是由于其他应用程序正在运行,那么您无能为力(假设您无法提高程序的优先级)。 您能解释一下如何为这种特定情况预分配内存吗? @ayadev - 如果您的程序可以使用固定或最大内存量运行,请在程序开始时分配所有内存,并且在程序退出之前不要释放任何内存。您可能需要在程序中创建某种内部内存管理。 我再次编辑了我的问题。您可以自己编译代码以查看您的操作系统的结果,我的是 Windows 7。我不知道如何为 int、long 和 timespec 变量分配内存..你能给我一个适合你的例子吗?谢谢..

以上是关于如何在 C(Linux)中的 while 循环中准确睡眠?的主要内容,如果未能解决你的问题,请参考以下文章

在C中的while循环或for循环之后没有代码执行[重复]

Linux C 编程学习第四天_循环语句_while_do/while_for_goto

Linux-文件的编辑和计划任务

Linux shell while read line

Linux Shell:Shell循环语句

c语言中的do-while循环怎么用啊?给个例子呗。