C语言怎么实现定时器

太空11 2009-09-16 03:20:36
C语言怎么实现定时器 比如我现在要3分钟执行一次我的函数 怎么做。
...全文
3124 16 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
csan 2009-09-17
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 hairetz 的回复:]
你可以直接调用alarm。
其本质就是计时加上信号触发。

此外,select计时更精确,虽然不是专门用来计时的
[/Quote]

很精辟!
  • 打赏
  • 举报
回复
你可以直接调用alarm。
其本质就是计时加上信号触发。

此外,select计时更精确,虽然不是专门用来计时的
atiansk2006 2009-09-17
  • 打赏
  • 举报
回复
该程序的ITMER_REAL定时器,每隔2秒钟都会发送一个SIGALRM信号,当主函数接收到了这个信号之后,调用信号处理函数prompt_info在标准错误上输出time is running out这个字符串。
对于ITIMER_VIRTUAL和ITIMER_PROF的使用方法类似,当你在setitimer里面设置的定时器为ITIMER_VIRTUAL的时候,你把sigaction里面的SIGALRM改为SIGVTALARM, 同理,ITIMER_PROF对应SIGPROF。
不过,你可能会注意到,当你用ITIMER_VIRTUAL和ITIMER_PROF的时候,你拿一个秒表,你会发现程序输出字符串的时间间隔会不止2秒,甚至5-6秒才会输出一个,至于为什么,自己好好琢磨一下^_^
下面我们来看看用sleep以及usleep怎么实现定时执行任务。
下载: timer2.c
1. #include <signal.h>
2. #include <unistd.h>
3. #include <string.h>
4. #include <stdio.h>
5.
6. staticchar msg[] = "I received a msg.\n";
7. int len;
8. void show_msg(int signo)
9. {
10.     write(STDERR_FILENO, msg, len);
11. }
12. intmain()
13. {
14.     structsigaction act;
15.     unionsigval tsval;
16.
17.     act.sa_handler = show_msg;
18.     act.sa_flags = 0;
19.     sigemptyset(&act.sa_mask);
20.     sigaction(50, &act, NULL);
21.
22.     len = strlen(msg);
23.     while( 1 )
24.     {
25.         sleep(2); /*睡眠2秒*/
26.         /*向主进程发送信号,实际上是自己给自己发信号*/
27.         sigqueue(getpid(), 50, tsval);
28.     }
29.     return0;
30. }
看到了吧,这个要比上面的简单多了,而且你用秒表测一下,时间很准,指定2秒到了就给你输出一个字符串。所以,如果你只做一般的定时,到了时间去执行一个任务,这种方法是最简单的。
下面我们来看看,通过自己计算时间差的方法来定时:
下载: timer3.c
1. #include <signal.h>
2. #include <unistd.h>
3. #include <string.h>
4. #include <stdio.h>
5. #include <time.h>
6.
7. staticchar msg[] = "I received a msg.\n";
8. int len;
9. static time_t lasttime;
10. void show_msg(int signo)
11. {
12.     write(STDERR_FILENO, msg, len);
13. }
14. intmain()
15. {
16.     structsigaction act;
17.     unionsigval tsval;
18.
19.     act.sa_handler = show_msg;
20.     act.sa_flags = 0;
21.     sigemptyset(&act.sa_mask);
22.     sigaction(50, &act, NULL);
23.
24.     len = strlen(msg);
25.     time(&lasttime);
26.     while( 1 )
27.     {
28.         time_tnowtime;
29.         /*获取当前时间*/
30.         time(&nowtime);
31.         /*和上一次的时间做比较,如果大于等于2秒,则立刻发送信号*/
32.         if(nowtime - lasttime >= 2)
33.         {
34.             /*向主进程发送信号,实际上是自己给自己发信号*/
35.             sigqueue(getpid(), 50, tsval);
36.             lasttime = nowtime;
37.         }       
38.     }
39.     return0;
40. }
这个和上面不同之处在于,是自己手工计算时间差的,如果你想更精确的计算时间差,你可以把 time 函数换成gettimeofday,这个可以精确到微妙。
上面介绍的几种定时方法各有千秋,在计时效率上、方法上和时间的精确度上也各有不同,采用哪种方法,就看你程序的需要
atiansk2006 2009-09-17
  • 打赏
  • 举报
回复


#include <sys/time.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>

static char msg[] = "time is running out.\n";
static int len;


/* time's up */
void prompt_info (int signo)
{
write (STDERR_FILENO, msg, len);
}

void init_sigaction (void)
{
struct sigaction tact;

tact.sa_handler = prompt_info;
tact.sa_flags = 0;

sigemptyset (&tact.sa_mask);
sigaction (SIGALRM, &tact, NULL);
}

void init_time ()
{
struct itimerval value;

value.it_value.tv_sec = 2;
value.it_value.tv_usec = 0;

value.it_interval = value.it_value;

/* set ITIMER_REAL */
setitimer (ITIMER_REAL, &value, NULL);
}

int main (int argc, char** argv)
{
len = strlen (msg);
init_sigaction ();
init_time ();
while (1);

exit (0);
}


atiansk2006 2009-09-17
  • 打赏
  • 举报
回复
网上找到的, 我整理了一下:


使用定时器的目的无非是为了周期性的执行某一任务, 或者是到了一个指定时间去执行某一任务. 要达到这一目的, 一般有两个常见的比较有效的方法. 一个是用Linux内部的三个定时器, 另一个是用sleep, usleep函数让进程睡眠一段时间, 其实还有一个方法, 那就是用gettimeofday, difftime等自己来计算时间间隔, 然后时间到了就执行某一任务, 但这种方法效率低, 所以不常用.

Linux 为每个进程提供的3个内部计时器:
ITIMER_REAL: 给一个指定的时间间隔, 按照实际的时间来减少这个计数, 当时间间隔为0的时候发出SIGALRM信号.
ITIMER_VIRTUAL: 给定一个时间间隔, 当进程执行的时候才减少计数, 时间间隔为0的时候发出SIGVTALRM信号.
ITIMER_PROF: 给定一个时间间隔, 当进程执行或者是系统为进程调度的时候, 减少计数, 时间到了, 发出SIGPROF信号, 这个和ITIMER_VIRTVAL联合, 常用来计算系统内核时间和用户时间.

常用到的函数:
#include <sys/time.h>
int getitimer (int which, struct itimerval* value);
int setitimer (int which, struct itimerval* newvalue, struct itimerval* oldvalue);

struct timeval
{
long tv_sec; /* 秒 */
long tv_usec; /* 微秒 */
};

struct itimerval
{
struct timeval it_interval; /* 时间间隔 */
struct timeval it_value; /* 当前时间计数 */
};
it_interval用来指定每隔多长时间执行任务, it_value用来保存当前时间离执行任务还有多长时间. 比如说, 你指定it_interval为2秒(微秒为0), 开始的时候我们把it_value的时间也设定为2秒(微秒为0), 当过了一秒, it_value就减少一个为1, 再过1秒, 则it_value又减少1, 变为0, 这个时候发出信号(告诉用户时间到了, 可以执行任务了), 并且系统自动把it_value的置重置为it_interval的值, 即2秒, 再重新计数.
jhams 2009-09-17
  • 打赏
  • 举报
回复
#include <unistd.h>
int pause(void);
pause 函数使调用进程挂起直到有信号递达。如果信号的处理动作是终止进
程,则进程终止,pause 函数没有机会返回;如果信号的处理动作是忽略,则
进程继续处于挂起状态,pause 不返回;如果信号的处理动作是捕捉,则调用
了信号处理函数之后 pause 返回-1,errno 设置为 EINTR,所以 pause 只有出错
的返回值(想想以前还学过什么函数只有出错返回值?)。错误码 EINTR 表示
“被信号中断”。
下面我们用 alarm 和 pause 实现 sleep(3)函数,称为 mysleep。
mysleep
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
void sig_alrm(int signo)
{
/* nothing to do */
}
unsigned int mysleep(unsigned int nsecs)
{

struct sigaction newact, oldact;
unsigned int unslept;
newact.sa_handler = sig_alrm;
sigemptyset(&newact.sa_mask);
newact.sa_flags = 0;
sigaction(SIGALRM, &newact, &oldact);
alarm(nsecs);
pause();
unslept = alarm(0);
sigaction(SIGALRM, &oldact, NULL);
return unslept;
}
int main(void)
{
while(1){
mysleep(2);
printf("Two seconds passed\n");
}
return 0;
}
1. main 函数调用 mysleep 函数,后者调用 sigaction 注册了 SIGALRM 信号
的处理函数 sig_alrm。
2. 调用 alarm(nsecs)设定闹钟。
3. 调用 pause 等待,内核切换到别的进程运行。
4. nsecs 秒之后,闹钟超时,内核发 SIGALRM 给这个进程。
5. 从内核态返回这个进程的用户态之前处理未决信号,发现有 SIGALRM 信
号,其处理函数是 sig_alrm。
6. 切换到用户态执行 sig_alrm 函数,进入 sig_alrm 函数时 SIGALRM 信号
被自动屏蔽,从 sig_alrm 函数返回时 SIGALRM 信号自动解除屏蔽。然后
自动执行系统调用 sigreturn 再次进入内核,再返回用户态继续执行进
程的主控制流程(main 函数调用的 mysleep 函数)。
7. pause 函数返回-1,然后调用 alarm(0)取消闹钟,调用 sigaction 恢复
SIGALRM 信号以前的处理动作。
jhams 2009-09-17
  • 打赏
  • 举报
回复
SIGPIPE 是一种由软件条件产生的信号,在例 3.7 “管道”中已经介绍过了。
本节主要介绍 alarm 函数和 SIGALRM 信号。
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
调用 alarm 函数可以设定一个闹钟,也就是告诉内核在 seconds 秒之后给当前
进程发 SIGALRM 信号,该信号的默认处理动作是终止当前进程。这个函数的返
回值是 0 或者是以前设定的闹钟时间还余下的秒数。打个比方,某人要小睡一
觉,设定闹钟为 30 分钟之后响,20 分钟后被人吵醒了,还想多睡一会儿,于
是重新设定闹钟为 15 分钟之后响,“以前设定的闹钟时间还余下的时间”就是
10 分钟。如果 seconds 值为 0,表示取消以前设定的闹钟,函数的返回值仍然
是以前设定的闹钟时间还余下的秒数。
例 6.1. alarm
#include <unistd.h>
#include <stdio.h>
int main(void)
{
int counter;
alarm(1);
for(counter=0; 1; counter++)
printf("counter=%d ", counter);
return 0;
}
这个程序的作用是 1 秒钟之内不停地数数,1 秒钟到了就被 SIGALRM 信号终
止。
guoshimin57 2009-09-16
  • 打赏
  • 举报
回复
这个玩意就是一种无奈,很多时候都要在可移植性和效率中作二选一。
bourbaki 2009-09-16
  • 打赏
  • 举报
回复
如果是c++,可以参考这个,里面有一个现成的类,或者用boost::asio库
http://stackoverflow.com/questions/503866/timer-class-in-linux
DaGod123 2009-09-16
  • 打赏
  • 举报
回复
mark
delphiwcdj 2009-09-16
  • 打赏
  • 举报
回复
学习
xiaocai0001 2009-09-16
  • 打赏
  • 举报
回复

// init timer
void TimerFactory::init_timer()
{
struct itimerval value;
value.it_value.tv_sec = 1; // min interval 1 second
value.it_value.tv_usec = 0;
value.it_interval = value.it_value;
Setitimer(ITIMER_REAL, &value, NULL );
return;
}

// init signal
void TimerFactory::init_signal()
{
Signal(SIGALRM, on_alarm);
return;
}

// callback to do things when SIGALARM arrived
void TimerFactory::on_alarm(int signo)
{
// do things you like here.
return;
}


steptodream 2009-09-16
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 guoshimin57 的回复:]
一种不可移植的方法是调用sleep(),一种可移植但不优雅的方法是:

/* 前文再续 */
调用gettimeofday()取得此刻的时间
while(1)
{
  调用gettimeofday()取得此刻的时间
  if(两个时间差满足你的要求) break;
}
/* 书接下一回 */
[/Quote]
不推荐使用 有时候会导致CPU利用率老高了

用1楼说的alarm()与setitimer()
具体用法google一下 写个简单的代码练习一下就会了
pottichu 2009-09-16
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 guoshimin57 的回复:]
/* 前文再续 */
调用gettimeofday()取得此刻的时间
while(1)
{
调用gettimeofday()取得此刻的时间
if(两个时间差满足你的要求) break;
}
/* 书接下一回 */
[/Quote]

这个还是不要了吧, 效率过于低下。

guoshimin57 2009-09-16
  • 打赏
  • 举报
回复
一种不可移植的方法是调用sleep(),一种可移植但不优雅的方法是:

/* 前文再续 */
调用gettimeofday()取得此刻的时间
while(1)
{
调用gettimeofday()取得此刻的时间
if(两个时间差满足你的要求) break;
}
/* 书接下一回 */
pottichu 2009-09-16
  • 打赏
  • 举报
回复
Linux下的定时器:alarm()与setitimer()

http://blog.csdn.net/feiyinziiuxx/archive/2009/08/26/4488140.aspx

23,217

社区成员

发帖
与我相关
我的任务
社区描述
Linux/Unix社区 应用程序开发区
社区管理员
  • 应用程序开发区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧