在c中高效暂停执行的最佳方法

在c中高效暂停执行的最佳方法,c,linux,state-machine,C,Linux,State Machine,我在Linux上运行的库中有一个状态机实现。程序的主循环是简单地等待,直到有足够的时间需要下一次执行状态机 目前,我有一个类似于以下psuedo代码的循环: while( 1 ) { while( StateTicks() > 0 ) StateMachine(); Pause( 10ms ); } 其中,StateTicks可能每隔50毫秒左右返回一次滴答声。Pause()越短,我在程序中使用的CPU时间就越多 有没有更好的方法来测试一段时间的流逝,也

我在Linux上运行的库中有一个状态机实现。程序的主循环是简单地等待,直到有足够的时间需要下一次执行状态机

目前,我有一个类似于以下psuedo代码的循环:

while( 1 )
{
    while( StateTicks() > 0 )
        StateMachine();

    Pause( 10ms );
}
其中,StateTicks可能每隔50毫秒左右返回一次滴答声。Pause()越短,我在程序中使用的CPU时间就越多

有没有更好的方法来测试一段时间的流逝,也许是基于信号?我宁愿在StateTicks()大于0之前停止执行,也不愿进行Pause()调用

在状态机实现的引擎罩下,StateTicks使用了运行良好的。我非常希望保持这种计时,因为如果StateMachine()调用所花的时间比StateMachine调用所花的时间长,那么这个实现将迎头赶上

暂停用于获得合理准确的暂停时间


也许这已经是最好的方法了,但它看起来并不特别优雅。

使用创建一个周期计时器,并让它调用“计时器滴答”信号量

为了避免丢失滴答声,我建议使用,可能是
SIGRTMIN+0
SIGRTMAX-0
。是,因此您可以在信号处理程序中安全地使用它

您的状态机只是等待信号量;不需要其他计时。如果处理勾号花费的时间太长,以下内容将不会被阻止,但会立即返回。本质上,信号量统计“丢失”的滴答声

示例代码(未测试!):

如果您支持多个并发状态,那么一个信号处理程序就足够了。你所在的州通常包括

    timer_t          timer;
    sem_t            semaphore;
    struct timespec  interval;
唯一棘手的事情是确保在破坏信号将访问的状态时没有挂起的计时器信号

由于信号传递将中断用于信号传递的线程中的任何阻塞I/O,因此您可能希望在库中设置一个特殊线程来处理计时器滴答声实时信号,并在所有其他线程中阻塞实时信号。您可以标记库初始化函数,以便它在
main()
之前自动执行

最佳情况下,您应该使用为信号传递执行勾号处理的同一线程。否则,如果信号是使用与正在运行滴答处理的CPU内核不同的CPU内核发送的,则滴答处理中会出现一些小的抖动或延迟


他的回答让我想起了等待和信号传递所涉及的延迟:如果你使用and,你可以调整睡眠时间来解释典型的延迟

下面是一个使用
clock\u gettime(clock\u MONOTONIC,)
nanosleep()的快速测试程序。

以50Hz的滴答频率进行500次滴答测试

./test 50 500 | sort -k 4
显示在我的机器上,在所需时刻的0.051 ms(51µs)内接受滴答声。即使降低优先级似乎也没有多大影响。以5kHz频率(0.2ms/滴答声)使用5000滴答声进行测试

产生类似的结果——尽管我没有检查在运行期间如果机器负载发生变化会发生什么

换句话说,在一台机器上进行的初步测试表明这可能是一个可行的选择,因此您可能希望在不同的机器和不同的负载下测试该方案。这比我在自己的机器上预期的要精确得多(Ubuntu 3.11.0-24-generic在x86_64上,运行在AMD Athlon II X4 640 CPU上)

这种方法有一个有趣的特性,即您可以轻松地使用单个线程来维护多个状态,即使它们使用不同的滴答率。您只需检查下一个勾号出现在哪个状态(最早的
->next
时间),
nanosleep()
(如果将来出现这种情况),然后处理勾号,将该状态提前到下一个勾号

问题?

除了:

如果暂停时间为几毫秒,您可以使用或(您可以计算剩余的睡眠时间,例如使用with
CLOCK\u REALTIME

如果你关心代码< > StimeMaChan[可能需要几毫秒(或者一毫秒的大一部分),并且你需要精确的10毫秒时间,那么可以考虑使用一个使用Linux特定的

的<代码>投票/代码>。
另请参见,和,答案(关于
民意测验
等的问题)

非常好的答案,非常感谢。这给了我很多思考的东西。我在每个进程中使用多个状态机,但这应该适用于摆脱Pause()语句。多谢各位。
    timer_t          timer;
    sem_t            semaphore;
    struct timespec  interval;
#define _POSIX_C_SOURCE 200809L
#include <sys/select.h>
#include <time.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

static const long   tick_latency = 75000L; /* 0.75 ms */
static const long   tick_adjust  = 75000L; /* 0.75 ms */

typedef struct {
    struct timespec  next;
    struct timespec  tick;
} state;

void state_init(state *const s, const double ticks_per_sec)
{
    if (ticks_per_sec > 0.0) {
        const double interval = 1.0 / ticks_per_sec;
        s->tick.tv_sec = (time_t)interval;
        s->tick.tv_nsec = (long)(1000000000.0 * (interval - (double)s->tick.tv_sec));
        if (s->tick.tv_nsec < 0L)
            s->tick.tv_nsec = 0L;
        else
        if (s->tick.tv_nsec > 999999999L)
            s->tick.tv_nsec = 999999999L;
    } else {
        s->tick.tv_sec = 0;
        s->tick.tv_nsec = 0L;
    }
    clock_gettime(CLOCK_MONOTONIC, &s->next);
}

static unsigned long count;

double state_tick(state *const s)
{
    struct timespec now, left;

    /* Next tick. */
    s->next.tv_sec += s->tick.tv_sec;
    s->next.tv_nsec += s->tick.tv_nsec;
    if (s->next.tv_nsec >= 1000000000L) {
        s->next.tv_nsec -= 1000000000L;
        s->next.tv_sec++;
    }

    count = 0UL;

    while (1) {

        /* Get current time. */
        clock_gettime(CLOCK_MONOTONIC, &now);

        /* Past tick time? */
        if (now.tv_sec > s->next.tv_sec ||
            (now.tv_sec == s->next.tv_sec &&
             now.tv_nsec >= s->next.tv_nsec - tick_latency))
            return (double)(now.tv_sec - s->next.tv_sec)
                 + (double)(now.tv_nsec - s->next.tv_nsec) / 1000000000.0;

        /* Calculate duration to wait */
        left.tv_sec = s->next.tv_sec - now.tv_sec;
        left.tv_nsec = s->next.tv_nsec - now.tv_nsec - tick_adjust;
        if (left.tv_nsec >= 1000000000L) {
            left.tv_nsec -= 1000000000L;
            left.tv_sec++;
        } else
        if (left.tv_nsec < -1000000000L) {
            left.tv_nsec += 2000000000L;
            left.tv_sec += 2;
        } else
        if (left.tv_nsec < 0L) {
            left.tv_nsec += 1000000000L;
            left.tv_sec--;
        }

        count++;

        nanosleep(&left, NULL);
    }
}


int main(int argc, char *argv[])
{
    double  rate, jitter;
    long    ticks, i;
    state   s;
    char    dummy;

    if (argc != 3 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
        fprintf(stderr, "\n");
        fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
        fprintf(stderr, "       %s TICKS_PER_SEC TICKS\n", argv[0]);
        fprintf(stderr, "\n");
        return 1;
    }

    if (sscanf(argv[1], " %lf %c", &rate, &dummy) != 1 || rate <= 0.0) {
        fprintf(stderr, "%s: Invalid tick rate.\n", argv[1]);
        return 1;
    }
    if (sscanf(argv[2], " %ld %c", &ticks, &dummy) != 1 || ticks < 1L) {
        fprintf(stderr, "%s: Invalid tick count.\n", argv[2]);
        return 1;
    }

    state_init(&s, rate);
    for (i = 0L; i < ticks; i++) {
        jitter = state_tick(&s);
        if (jitter > 0.0)
            printf("Tick %9ld: Delayed   %9.6f ms, %lu sleeps\n", i+1L, +1000.0 * jitter, count);
        else
        if (jitter < 0.0)
            printf("Tick %9ld: Premature %9.6f ms, %lu sleeps\n", i+1L, -1000.0 * jitter, count);
        else
            printf("Tick %9ld: Exactly on time, %lu sleeps\n", i+1L, count);
        fflush(stdout);
    }

    return 0;
}
gcc -O2 test.c -lrt -o test
./test 50 500 | sort -k 4
nice -n 19 ./test 5000 5000 | sort -k 4