how to sleep accurately in a while loop in C (Linux)?

坚强是说给别人听的谎言 提交于 2019-12-11 01:49:42

问题


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..

EDIT:

#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;
}

RESULT (log_file):

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


回答1:


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).

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


来源:https://stackoverflow.com/questions/29600621/how-to-sleep-accurately-in-a-while-loop-in-c-linux

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!