如何在C(Linux)中的while循环中准确地入睡? [英] how to sleep accurately in a while loop in C (Linux)?

查看:130
本文介绍了如何在C(Linux)中的while循环中准确地入睡?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在C代码(Linux OS)中,我需要在while循环内准确地睡眠-例如,10000微秒,共1000次.我尝试了usleep,nanosleep,select,pselect和其他一些方法,但均未成功. 一旦进入〜50次,它的睡眠时间就会延长100%(〜20000 us). 每次延迟后,我需要执行一个操作.因此,每个延迟都必须非常准确. 有没有一种方法可以在这种情况下进行准确的睡眠? 谢谢.

In a C code (Linux OS), I need to sleep accurately inside a while loop - say, 10000 microseconds for 1000 times. I have tried usleep, nanosleep, select, pselect, and some other methods with no success. Once in ~50 times it would sleep %100 longer (~20000 us). I need to perform an action after each delay. So, each delay has to be very accurate. Is there a way to do accurate sleeps for this case? Thanks..

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

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 err = 42
i = 16 err = -51
i = 17 err = -19
i = 18 err = -12
i = 19 err = 4
i = 20 err = 12
i = 21 err = -36
i = 22 err = -38
i = 23 err = 18
i = 24 err = 1
i = 25 err = -21
i = 26 err = -37
i = 27 err = 31
i = 28 err = -4
i = 29 err = -45
i = 30 err = -37
i = 31 err = 20
i = 32 err = -10
i = 33 err = -5
i = 34 err = -12
i = 35 err = -5
i = 36 err = -10
i = 37 err = -12
i = 38 err = -2
i = 39 err = 14
i = 40 err = -34
i = 41 err = -10
i = 42 err = -6
i = 43 err = 15
i = 44 err = -34
i = 45 err = -12
i = 46 err = -15
i = 47 err = -25
i = 48 err = 11614
i = 49 err = 2340
i = 50 err = 589
i = 51 err = 12254
i = 52 err = -93
i = 53 err = -19

推荐答案

睡眠较短的时间,然后轮询高频计数器以完成延迟.将所有延迟都基于计数器的原始读数,以防止随时间推移而发生漂移.例如,previous_count = counter(),wait_count = previous_count + delay_count,延迟到counter()-wait_count> = 0,previous_count + = delay_count,....如果所需的频率不是counter()频率的精确倍数,请使用一个变量作为延迟的小数部分(我称此为"remainder_count").

Sleep for a shorter period of time, then poll a high frequency counter to complete the delay. Base all the delays off an original reading of the counter to prevent drift over time. For example, previous_count = counter(), wait_count = previous_count + delay_count, delay until counter() - wait_count >= 0, previous_count += delay_count, ... . If the desired frequency is not an exact multiple of the counter() frequency, then use a variable for the fractional part of the delay (I call this remainder_count).

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

Since you mentioned Windows, here is an example of a thread running at a fixed frequency, and Windows XP compatable, where a Sleep(1) can take up to 2 ms. dwLateStep is a debug aid, incremented every time a cycle takes too long (it will catch up if possible). The delays are based on an original reading of the counter (using uWait, uRem, uPrev, uDelta), so there's no drift over a long period of time. This is basically how games implement a fixed frequency physics thread. Note that .net framework is not used for these type of games because it intermittently pauses a process in order to repack paged memory. Such games will set the physics thread priority higher than normal to keep other threads or processes from interfering with it.

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 */

这篇关于如何在C(Linux)中的while循环中准确地入睡?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆