用习惯了内核层的定时器,发现应用层的各种定时器都各种优缺点,感觉没有内核的定时器用起来顺手。总结一下,可以根据不同的场景灵活选择。
1.alarm
alarm 函数原型 unsigned int alarm(unsigned int seconds)
alarm是通过发送信号SIGALRM 来实现定时的,就是在指定seconds秒后向进程发送
SIGALRM信号,只要通过注册信号及其信号处理函数就可以实现定时处理任务了。alarm是一个不怎么精确的定时器。进程调用alarm后,任何本进程以前的alarm()调用都将无效。如果seconds为0,清空本进程的所有alarm 定时器。alarm 函数的返回值:如果调用alarm前,进程中已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回0。 例子如下:
-
#include <stdio.h>
-
#include <unistd.h>
-
#include <signal.h>
-
-
void sigalrm_fn(int sig)
-
{
-
printf("alarm!\n");
-
return;
-
}
-
-
int main(int argc,char **argv)
-
{
-
signal(SIGALRM, sigalrm_fn);
-
alarm(2);
-
-
while(1);
-
}
但是如果在多线程中使用alarm 很容易出现混乱,达不到预期结果。如果还是想用类似的机制的话,可以重写一个alarm函数 用pthread_kill发送alarm signal,或者结合phread_sigmask 来屏蔽某些信号,不过多线程建议不用信号。
2.setitimer
setitimer的精度比alarm的要高,而且可以实现延时(任务第一次执行时间)和定时(任务执行间隔时间)
setitimer的函数原型int setitimer(int which, const struct itimerval *value,struct itimerval *ovalue)
每一个进程都
which可以选择以下三个类型之一
ITIMER_REAL : 以系统真实的时间来计算,超时后会发送SIGALRM信号。
ITIMER_VIRTUAL : 以该进程在用户态下花费的时间来计算,
超时后会发送SIGVTALRM信号。
ITIMER_PROF : 以该进程在用户态下和内核态下所费的时间来计算,
超时后会发送SIGPROF信号
struct itimerval 数据结构如下:
struct itimerval {
struct timeval it_interval; /* 任务定时执行间隔,这个如果为0,则执行一次 */
struct timeval it_value; /* 任务延时执行时间,这个值必须大于0,如果为0 ,则任务则不会执行*/
};
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
第二个参数用来指定定时器第一次执行时间,和之后的执行间隔,第三个参数ovalue 用来记录上一次调用setitimer设置的value
一般不用,
返回值:setitimer 执行成功返回0,错误返回-1。
例子:
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <unistd.h>
-
#include <signal.h>
-
#include <time.h>
-
#include <sys/time.h>
-
-
void sigroutine(int signo){
-
switch (signo){
-
case SIGALRM:
-
printf("Catch a signal -- SIGALRM \n");
-
signal(SIGALRM, sigroutine);
-
break;
-
case SIGVTALRM:
-
printf("Catch a signal -- SIGVTALRM \n");
-
signal(SIGVTALRM, sigroutine);
-
break;
-
case SIGPROF:
-
printf("Catch a signal -- SIGPROF \n");
-
signal(SIGPROF, sigroutine);
-
break;
-
}
-
return;
-
}
-
-
int main(int argc, char ** argv)
-
{
-
struct itimerval value,value2,value3;
-
-
signal(SIGALRM, sigroutine);
-
signal(SIGVTALRM, sigroutine);
-
signal(SIGPROF, sigroutine);
-
value.it_value.tv_sec = 1;
-
value.it_value.tv_usec = 0;
-
value.it_interval.tv_sec = 1;
-
value.it_interval.tv_usec = 0;
-
setitimer(ITIMER_REAL, &value, NULL);
-
value2.it_value.tv_sec = 0 ;
-
value2.it_value.tv_usec = 400000;
-
value2.it_interval.tv_sec = 0;
-
value2.it_interval.tv_usec = 400000;
-
setitimer(ITIMER_VIRTUAL, &value2, NULL);
-
value3.it_value.tv_sec = 0 ;
-
value3.it_value.tv_usec = 500000;
-
value3.it_interval.tv_sec = 0;
-
value3.it_interval.tv_usec = 500000;
-
setitimer(ITIMER_PROF, &value3, NULL);
-
for(;;);
-
}
3.sleep/usleep/nanosleep
这三个函数的使用都是比较简单的,直接调用就可以。
sleep:精度是秒,如果睡眠到指定时间,则返回0,如果被中断,则返回剩余秒。
usleep:精度是微妙,返回值跟sleep类似
nanosleep:精度是纳秒,如果睡眠到指定时间,返回0,如果被中断,返回-1,剩余时间数,记录在第三个参数
这三者的用法都比较简单,不举例子了
其中alarm 和 settimer 底层是用同样的定时器的,因此调用了其中一个,会影响另外一个的调用。
sleep 也有可能用用信号SIGALRM,不建议alarm 和sleep 混着来用。
4.select
select 可以实现精度为微妙的定时器,而且在多线程使用也简单
select 函数原型:int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
select 函数有四种可能会返回:readfds 有可读句柄,writefds有可写句柄,exceptfds 有异常条件发生的句柄,或者timeout时间到了。想用select 作为定时器,只要前面三个集合都置为NULL,则返回条件就剩下超时了。而struct timeval 可以精确到的时间是微妙的,因此select实现的定时器精度也能到微妙的。
例子如下:
-
#include <stdio.h>
-
#include <sys/time.h>
-
#include <errno.h>
-
-
void seconds_timer(unsigned seconds){
-
struct timeval tv;
-
tv.tv_sec=seconds;
-
tv.tv_usec=0;
-
int err;
-
do{
-
err=select(0,NULL,NULL,NULL,&tv);
-
}while(err<0 && errno==EINTR);
-
}
-
-
void milliseconds_timer(unsigned long mSec){
-
struct timeval tv;
-
tv.tv_sec=mSec/1000;
-
tv.tv_usec=(mSec%1000)*1000;
-
int err;
-
do{
-
err=select(0,NULL,NULL,NULL,&tv);
-
}while(err<0 && errno==EINTR);
-
}
-
-
void microseconds_timer(unsigned long uSec){
-
struct timeval tv;
-
tv.tv_sec=uSec/1000000;
-
tv.tv_usec=uSec%1000000;
-
int err;
-
do{
-
err=select(0,NULL,NULL,NULL,&tv);
-
}while(err<0 && errno==EINTR);
-
}
-
-
int main(int argc,char **argv)
-
{
-
int i;
-
for(i=0;i<5;++i){
-
printf("%d\n",i);
-
seconds_timer(1);
-
milliseconds_timer(1500);
-
microseconds_timer(1900000);
-
}
-
}
阅读(1804) | 评论(0) | 转发(0) |