【问题标题】:how to sleep accurately in a while loop in C (Linux)?如何在 C(Linux)中的 while 循环中准确睡眠?
【发布时间】: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 = -146
i = 1 err = -207
i = 2 err = -8
i = 3 err = -4
i = 4 err = -22
i = 5 err = 31
i = 6 err = -45
i = 7 err = 9
i = 8 err = 61
i = 9 err = -71
i = 10 err = -24
i = 11 err = 14
i = 12 err = -12
i = 13 err = -32
i = 14 err = -15
i = 15 错误 = 42
i = 16 错误 = -51
i = 17 错误 = -19
i = 18 错误 = -12
i = 19 错误 = 4
i = 20错误 = 12
i = 21 错误 = -36
i = 22 错误 = -38
i = 23 错误 = 18
i = 24 错误 = 1
i = 25 错误 = - 21
i = 26 err = -37
i = 27 err = 31
i = 28 err = -4
i = 29 err = -45
i = 30 err = -37
i = 31 错误 = 20
i = 32 错误 = -10
i = 33 错误 = -5
i = 34 错误 = -12
i = 35 错误 = -5i = 36 err = -10
i = 37 err = -12
i = 38 err = -2
i = 39 err = 14
i = 40 err = -34
i = 41 错误 = -10
i = 42 错误 = -6
i = 43 错误 = 15
i = 44 错误 = -34
i = 45 错误 = -12
i = 46 错误 = -15
i = 47 错误 = -25
i = 48 错误 = 11614
i = 49 错误 = 2340
i = 50 错误 = 589
i = 5 1 错误 = 12254
i = 52 错误 = -93
i = 53 错误 = -19

【问题讨论】:

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

标签: android c sleep precision


【解决方案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 变量分配内存..你能给我一个适合你的例子吗?谢谢..
猜你喜欢
  • 2017-10-01
  • 2012-07-24
  • 1970-01-01
  • 1970-01-01
  • 2019-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多