【问题标题】:Calling a function at exact every second using a timer?使用计时器准确地每秒调用一个函数?
【发布时间】:2012-01-03 14:48:12
【问题描述】:

我需要每秒调用一个函数,因为我想基于每秒存储数据,所以我不能错过第二个? C中最好的方法是什么?

下面是timer_create方法的骨架,这个够靠谱吗?

#include <stdio.h>
#include <time.h>
#include <signal.h>

timer_t gTimerid;

void start_timer(void)
{        
  struct itimerspec value;

  value.it_value.tv_sec = 1;
  value.it_value.tv_nsec = 0;  
  value.it_interval.tv_sec = 1;
  value.it_interval.tv_nsec = 0;

  timer_create (CLOCK_REALTIME, NULL, &gTimerid);    
  timer_settime (gTimerid, 0, &value, NULL);
}

void stop_timer(void)
{        
  struct itimerspec value;

  value.it_value.tv_sec = 0;
  value.it_value.tv_nsec = 0;    
  value.it_interval.tv_sec = 0;
  value.it_interval.tv_nsec = 0;

  timer_settime (gTimerid, 0, &value, NULL);        
}

void timer_callback(int sig)
{    
  printf(" Catched timer signal: %d ... !!\n", sig);     
}

int main(int ac, char **av)
{
    (void) signal(SIGALRM, timer_callback);
    start_timer();
    while(1);
}

【问题讨论】:

  • fork 标签的链接是什么?
  • 不可能准确地每 1 秒调用一次函数。即使您的 CPU 时钟从铯原子钟获得脉冲(即使原子钟也有公差,尽管非常小),也没有什么能阻止大型操作系统(例如 Linux、Windows、Mac)抢占您程序的线程。你愿意允许多大的容忍度?或者您是否考虑过不依赖于如此严格的公差的策略?
  • 您需要一个特定于平台的计时器。那么,您需要哪个平台?
  • 将您之前的一些问题标记为已回答。

标签: c timer signals


【解决方案1】:

在 Linux 和其他 POSIX 系统上,timer_create 是您正在寻找的功能。将定时器设置为通过信号传递,它将非常可靠。不要使用较旧的 ualarmsetitimer api,它们已被弃用,并且存在您可能不想遇到的各种丑陋问题,除非您已经是实时 unix 方面的专家...

【讨论】:

  • 亲爱的 R.. 所以 timer_create 是 time.h 库的一部分。那么它会在一秒钟内准确地调用该函数吗?
  • 我发现这个链接qnx.com/developers/docs/6.3.2/neutrino/lib_ref/t/…是你的意思吗?
  • @R 我添加了一些我在网上找到的代码可以用作我的骨架我不知道如何在回调函数中打印时间?
  • 我输入了代码,但它总是丢失,为什么?我还能把代码放在哪里?
  • @R 我现在已经更新了我的第一篇文章,如果可以的话,我的第一篇文章需要你的 cmets 代码吗?
【解决方案2】:

您有两个选项可以每隔一秒调用一次函数:

  • 做一个“忙碌的等待”
  • 让您的进程/线程休眠一段时间

第一个选项肯定更准确,但 CPU 消耗更多且响应速度更慢。只需使用whilefor 循环即可完成。

这里是这个 busy-wait 循环的一个小例子:

#include <time.h>

#define TIME_TO_WAIT 1 /* wait for one second */
...
clock_t last = clock();
while(1) {
    clock_t current = clock();
    if (current >= (last + TIME_TO_WAIT * CLOCKS_PER_SEC)) {
        yourSpecialFunction(); /* insert your function here */
        last = current;
    }
}

第二个选项可能不太准确(因为您的进程可能等待的时间可能比指定的时间少一点或多一点),但就多处理和调度而言,它是首选选项。您可以使用您的系统sleep()/usleep()/Sleep()(取决于您的系统)功能。或者,您可以使用信号。

【讨论】:

  • 所以你的建议是在一定时间醒来后去睡觉吧?
  • 据我所知,“忙等”是一种非常糟糕的做法,不是吗?
  • @Milo:这取决于用例。例如,在游戏中,它很常用,因为您希望分配尽可能多的 CPU 时间,以避免由于调度程序造成的微滞后。
  • @user837306:是的,完全正确。 sleep 函数(或它的派生词)将暂停您的进程一段给定的时间。在while-loop 中可以通过定期检查时间来实现同样的效果。
  • @constantinius 问题是我想在第二秒触发它并进行处理。紧接着下一秒我也想触发它进行处理等等这种方法会起作用吗?
【解决方案3】:

ualarm() 可能是最简单的方法。正如其他人所提到的,不能保证完美的准确性,但分辨率可能就足够了。

void each_sec(int x)
{
     printf("%d", (int)time(NULL));
}

int main()
{
    signal(SIGALRM, each_sec);
    ualarm(1000000, 1000000);
}

为了清晰起见,我使用了signal,但sigaction/sigprocmask 更便携,功能更丰富。

【讨论】:

  • 所以在这种情况下,尽管之前调用该函数的每一秒都在执行某个函数,但它会调用下一个函数吗?
  • 对。但是,当 each_sec 的执行时间超过一秒时,您会遇到奇怪的行为。
  • 戴夫,那么在这种情况下你建议什么最好的解决方案来避免奇怪的行为。
  • @user837306 确保each_sec 在一秒钟内运行。否则,您需要采取许多与编写线程程序相同的预防措施。您还需要使用sigprocmasksigaction 来确保您可以在SIGALRM 处理程序中接收SIGALRM
  • 如果它正在运行,下一秒需要调用它会不会有问题?
【解决方案4】:

在 Unix/Linux 上,您可以使用计时器,这是一个示例:

#include <stdio.h>
#include <signal.h>
#include <sys/time.h>



int limit = 10;
/* signal process */
void timeout_info(int signo)
{
   if(limit == 0)
   {
       printf("Sorry, time limit reached.\n");
       exit(0);
   }
   printf("only %d senconds left.\n", limit--);
}

/* init sigaction */
void init_sigaction(void)
{
    struct sigaction act;

    act.sa_handler = timeout_info;
    act.sa_flags   = 0;
    sigemptyset(&act.sa_mask);
    sigaction(SIGPROF, &act, NULL);
} 

/* init */
void init_time(void)
{
    struct itimerval val;

    val.it_value.tv_sec = 1;
    val.it_value.tv_usec = 0;
    val.it_interval = val.it_value;
    setitimer(ITIMER_PROF, &val, NULL);
}


int main(void)
{
    char *str;
    char c;

    init_sigaction();
    init_time();
    printf("You have only 10 seconds for thinking.\n");

    while(1);
    exit(0);
}

用你自己的函数替换timeout_info

【讨论】:

  • 来自man page:“定时器永远不会在请求的时间之前过期,但可能会在之后的一些(短)时间过期,这取决于系统定时器的分辨率和系统负载”。不过,这可能已经足够了。
  • @manuzhang 会在第一秒触发一次,并且每隔一秒就会触发一次,而不会打扰之前的通话。
  • @manuzhang 是 timer_create 一个更好的选择你能检查我用一些示例代码编辑了我的第一篇文章
【解决方案5】:

在 Linux 上,使用select() 进行计时是很常见的。通过这种方式,您还可以收到有关文件描述符活动的通知。

您需要有一个struct timeval 用于间隔并将其作为 select 的最后一个参数传递。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-08-29
    • 2014-12-24
    • 2018-10-12
    • 2020-07-13
    • 2013-09-07
    • 1970-01-01
    • 2011-11-06
    相关资源
    最近更新 更多