Chinaunix首页 | 论坛 | 博客
  • 博客访问: 234919
  • 博文数量: 31
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 296
  • 用 户 组: 普通用户
  • 注册时间: 2016-06-22 11:52
文章分类

全部博文(31)

文章存档

2018年(3)

2017年(11)

2016年(12)

2015年(5)

我的朋友

分类: C/C++

2017-08-11 15:40:27

用习惯了内核层的定时器,发现应用层的各种定时器都各种优缺点,感觉没有内核的定时器用起来顺手。总结一下,可以根据不同的场景灵活选择。

1.alarm
alarm 函数原型 unsigned int alarm(unsigned int seconds)
alarm是通过发送信号SIGALRM 来实现定时的,就是在指定seconds秒后向进程发送SIGALRM信号,只要通过注册信号及其信号处理函数就可以实现定时处理任务了。alarm是一个不怎么精确的定时器。进程调用alarm后,任何本进程以前的alarm()调用都将无效。如果seconds为0,清空本进程的所有alarm 定时器。alarm 函数的返回值:如果调用alarm前,进程中已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回0。 例子如下:

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <signal.h>

  4. void sigalrm_fn(int sig)
  5. {
  6.     printf("alarm!\n");
  7.     return;
  8. }

  9. int main(int argc,char **argv)
  10. {
  11.     signal(SIGALRM, sigalrm_fn);
  12.     alarm(2);
  13.     
  14.     while(1);
  15. }

但是如果在多线程中使用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。
例子:

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <signal.h>
  5. #include <time.h>
  6. #include <sys/time.h>

  7. void sigroutine(int signo){
  8.    switch (signo){
  9.    case SIGALRM:
  10.        printf("Catch a signal -- SIGALRM \n");
  11.        signal(SIGALRM, sigroutine);
  12.        break;
  13.    case SIGVTALRM:
  14.        printf("Catch a signal -- SIGVTALRM \n");
  15.        signal(SIGVTALRM, sigroutine);
  16.        break;
  17.    case SIGPROF:
  18.        printf("Catch a signal -- SIGPROF \n");
  19.        signal(SIGPROF, sigroutine);
  20.        break;
  21.    }
  22.    return;
  23. }

  24. int main(int argc, char ** argv)
  25. {
  26.    struct itimerval value,value2,value3;

  27.    signal(SIGALRM, sigroutine);
  28.    signal(SIGVTALRM, sigroutine);
  29.    signal(SIGPROF, sigroutine);
  30.    value.it_value.tv_sec = 1;
  31.    value.it_value.tv_usec = 0;
  32.    value.it_interval.tv_sec = 1;
  33.    value.it_interval.tv_usec = 0;
  34.    setitimer(ITIMER_REAL, &value, NULL);
  35.    value2.it_value.tv_sec = 0 ;
  36.    value2.it_value.tv_usec = 400000;
  37.    value2.it_interval.tv_sec = 0;
  38.    value2.it_interval.tv_usec = 400000;
  39.    setitimer(ITIMER_VIRTUAL, &value2, NULL);
  40.    value3.it_value.tv_sec = 0 ;
  41.    value3.it_value.tv_usec = 500000;
  42.    value3.it_interval.tv_sec = 0;
  43.    value3.it_interval.tv_usec = 500000;
  44.    setitimer(ITIMER_PROF, &value3, NULL);
  45.    for(;;);
  46. }

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实现的定时器精度也能到微妙的。
例子如下:

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <sys/time.h>
  3. #include <errno.h>

  4. void seconds_timer(unsigned seconds){
  5.     struct timeval tv;
  6.     tv.tv_sec=seconds;
  7.     tv.tv_usec=0;
  8.     int err;
  9.     do{
  10.        err=select(0,NULL,NULL,NULL,&tv);
  11.     }while(err<0 && errno==EINTR);
  12. }

  13. void milliseconds_timer(unsigned long mSec){
  14.     struct timeval tv;
  15.     tv.tv_sec=mSec/1000;
  16.     tv.tv_usec=(mSec%1000)*1000;
  17.     int err;
  18.     do{
  19.        err=select(0,NULL,NULL,NULL,&tv);
  20.     }while(err<0 && errno==EINTR);
  21. }

  22. void microseconds_timer(unsigned long uSec){
  23.     struct timeval tv;
  24.     tv.tv_sec=uSec/1000000;
  25.     tv.tv_usec=uSec%1000000;
  26.     int err;
  27.     do{
  28.         err=select(0,NULL,NULL,NULL,&tv);
  29.     }while(err<0 && errno==EINTR);
  30. }

  31. int main(int argc,char **argv)
  32. {
  33.     int i;
  34.     for(i=0;i<5;++i){
  35.         printf("%d\n",i);
  36.         seconds_timer(1);
  37.         milliseconds_timer(1500);
  38.         microseconds_timer(1900000);
  39.     }
  40. }


阅读(1804) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~