如何在C语言中使用定时器?

17

在C语言中使用计时器的方法是什么?我需要等待500毫秒来执行一个任务。请提供任何好的方法来完成这个任务。我曾经使用过sleep(3);,但这种方法在此时间段内不起作用。我有一些代码会在此期间尝试获取任何输入。


选择,轮询,epoll...你想要什么? - Eddy_Em
1
这是重复的问题。请参见https://dev59.com/7HRB5IYBdhLWcg3w77on - pcbabu
1
@Eddy_Em 这些 select、poll、epoll 是什么?我想设置一个计时器,比如 5 秒钟。如果用户在这段时间内输入,那么输入将被视为有效。 - user2332426
1
只需阅读“man termios”:您可以设置终端以等待5秒钟的输入。如果没有任何输入,“read”将返回0,否则您可以从终端读取用户数据。 - Eddy_Em
在纯标准C中无法使用定时器,需要一些操作系统支持。在Linux上,请仔细阅读time(7)。 - Basile Starynkevitch
4个回答

21

这是我使用的解决方案(它需要 #include <time.h>):


int msec = 0, trigger = 10; /* 10ms */
clock_t before = clock();

do {
  /*
   * Do something to busy the CPU just here while you drink a coffee
   * Be sure this code will not take more than `trigger` ms
   */

  clock_t difference = clock() - before;
  msec = difference * 1000 / CLOCKS_PER_SEC;
  iterations++;
} while ( msec < trigger );

printf("Time taken %d seconds %d milliseconds (%d iterations)\n",
  msec/1000, msec%1000, iterations);

CLOCKS_PER_SEC 是什么? - mLstudent33
2
@mLstudent33 这个宏是由时钟函数测量的每秒钟时钟滴答数 来源 - David Guyon
1
这是一个糟糕的解决方案,因为它会导致进程不必要地消耗处理器时间。 - Eric Postpischil

8
你可以使用来自 time.htime_t 结构和 clock() 函数。
通过使用 clock() 将开始时间存储在 time_t 结构中,并通过比较存储时间和当前时间之间的差异来检查经过的时间。

应该放在for循环内部吗? - user2332426
1
这取决于你想做什么。如果你的程序基于一个大循环,你可以把它放在里面。否则,你将不得不创建一个专用线程来循环你的计时器。 - Qutus
1
这是一个糟糕的解决方案,因为它会不必要地消耗处理器时间。 - Eric Postpischil

4

是的,你需要一个循环。如果你已经有了一个主循环(大多数GUI事件驱动程序都有),你可能可以将计时器放入其中。使用:

#include <time.h> 
time_t my_t, fire_t;

然后(超过1秒的时间),通过读取当前时间来初始化您的计时器:

my_t = time(NULL);

在计时器等待的秒数上添加数字并将其存储在fire_t中。time_t本质上是一个uint32_t,您可能需要进行强制类型转换。

在您的循环内执行另一个操作

my_t = time(NULL);

如果 (my_t > fire_t) 那么就认为定时器已经触发并在那里执行你想要的操作。这可能包括通过执行另一个 fire_t = time(NULL) + seconds_to_wait 来重置它以便下次使用。
time_t 是一种有些过时的 Unix 存储时间的方法,它将时间存储为自1970年1月1日午夜以来的秒数,但它具有许多优点。对于小于1秒的时间,您需要使用 gettimeofday()(微秒)或 clock_gettime()(纳秒),并处理一个 struct timeval 或 struct timespec,它是一个 time_t 和自那个 1 秒标记以来的微秒或纳秒。制作定时器的方式与此相同,只是当您添加等待时间时,如果得到的微秒或纳秒值超过了 1 秒,则需要手动进行进位(进入 time_t)。是的,这很麻烦。请参见 man 2 time、man gettimeofday、man clock_gettime。
sleep()、usleep()、nanosleep() 有一个隐藏的好处。你会看到它暂停了你的程序,但它们实际上会释放 CPU,使其空闲那么长时间。反复轮询通过读取时间并将其与完成时间进行比较(我们到了吗?)将消耗大量的 CPU 周期,这可能会减慢在同一台机器上运行的其他程序(并使用更多的电/电池)。最好大部分时间都 sleep(),然后再开始检查时间。
如果您想同时睡觉和工作,您需要线程。

2
也许这些示例可以帮助您。
#include <stdio.h>
#include <time.h>
#include <stdlib.h>


/*
    Implementation simple timeout

    Input: count milliseconds as number

    Usage:
        setTimeout(1000) - timeout on 1 second
        setTimeout(10100) - timeout on 10 seconds and 100 milliseconds
 */
void setTimeout(int milliseconds)
{
    // If milliseconds is less or equal to 0
    // will be simple return from function without throw error
    if (milliseconds <= 0) {
        fprintf(stderr, "Count milliseconds for timeout is less or equal to 0\n");
        return;
    }

    // a current time of milliseconds
    int milliseconds_since = clock() * 1000 / CLOCKS_PER_SEC;

    // needed count milliseconds of return from this timeout
    int end = milliseconds_since + milliseconds;

    // wait while until needed time comes
    do {
        milliseconds_since = clock() * 1000 / CLOCKS_PER_SEC;
    } while (milliseconds_since <= end);
}


int main()
{

    // input from user for time of delay in seconds
    int delay;
    printf("Enter delay: ");
    scanf("%d", &delay);

    // counter downtime for run a rocket while the delay with more 0
    do {
        // erase the previous line and display remain of the delay
        printf("\033[ATime left for run rocket: %d\n", delay);

        // a timeout for display
        setTimeout(1000);

        // decrease the delay to 1
        delay--;

    } while (delay >= 0);

    // a string for display rocket
    char rocket[3] = "-->";

    // a string for display all trace of the rocket and the rocket itself
    char *rocket_trace = (char *) malloc(100 * sizeof(char));

    // display trace of the rocket from a start to the end
    int i;
    char passed_way[100] = "";
    for (i = 0; i <= 50; i++) {
        setTimeout(25);
        sprintf(rocket_trace, "%s%s", passed_way, rocket);
        passed_way[i] = ' ';
        printf("\033[A");
        printf("| %s\n", rocket_trace);
    }

    // erase a line and write a new line
    printf("\033[A");
    printf("\033[2K");
    puts("Good luck!");

    return 0;
}

编译文件、运行并在之后删除(我的首选)。
$ gcc timeout.c -o timeout && ./timeout && rm timeout

请尝试自行运行以查看结果。

注:

测试环境

$ uname -a
Linux wlysenko-Aspire 3.13.0-37-generic #64-Ubuntu SMP Mon Sep 22 21:28:38 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
$ gcc --version
gcc (Ubuntu 4.8.5-2ubuntu1~14.04.1) 4.8.5
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

在Ubuntu中出现了“检测到缓冲区溢出:/home/comp/Qt_project/timer_in_C/timer_in_C已终止”的错误。我将for (i = 0; i <= 40; i++) {进行了更改,现在一切都正常工作了 :3 - timob256
2
这是一个糟糕的解决方案,因为它会导致进程不必要地消耗处理器时间。 - Eric Postpischil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接