原创 多线程中定时器的使用

2010-10-14 19:43 1506 4 4 分类: MCU/ 嵌入式

                                                                                                                ——华清远见 曹忠明


         不管是在进程还是线程,很多时候我们都会使用一些定时器之类的功能,这里就定时器在多线程的使用说一下。首先在linux编程中定时器函数有alarm()和setitimer(),alarm()可以提供一个基于秒的定时功能,而setitimer可以提供一个基于微妙的定时功能。


         alarm()原型:


         #include <unistd.h>


unsigned int alarm(unsigned int seconds);


这个函数在使用上很简单,第一次调用这个函数的时候是设置定时器的初值,下一次调用是重新设置这个值,并会返回上一次定时的剩余时间。


          setitimer()原型:


#include <sys/time.h>


int setitimer(int which, const struct itimerval *value,


                     struct itimerval *ovalue);


这个函数使用起来稍微有点说法,首先是第一个参数which的值,这个参数设置timer的计时策略,which有三种状态分别是:


ITIMER_REAL:使用系统时间来计数,时间为0时发出SIGALRM信号,这种定时能够得到一个精准的定时,当然这个定时是相对的,因为到了微秒级别我们的处理器本身就不够精确。


ITIMER_VIRTUAL:使用进程时间也就是进程分配到的时间片的时间来计数,时间为0是发出SIGVTALRM信号,这种定时显然不够准确,因为系统给进程分配时间片不由我们控制。


ITIMER_PROF:上面两种情况都能够触发


第二个参数参数value涉及到两个结构体:


struct itimerval {


       struct timeval it_interval;                  /* next value */


       struct timeval it_value;                   /* current value */


 };


 


struct timeval {


       long tv_sec;                        /* seconds */


       long tv_usec;                       /* microseconds */


};


在结构体itimerval中it_value是定时器当前的值,it_interval是当it_value的为0后重新填充的值。而timeval结构体中的两个变量就简单了一个是秒一个是微秒。


上面是这两个定时函数的说明,这个函数使用本不是很难,可以说是很简单,但是碰到具体的应用的时候可能就遇到问题了,在多进程编程中使用一般不会碰到什么问题,这里说的这些问题主要体现在多线程编程中。比如下面这个程序


#include <pthread.h>


#include <signal.h>


#include <stdio.h>


#include <stdlib.h>


#include <unistd.h>


#include <sys/time.h>


 


void sig_handler(int signo)


{


          alarm(2);


          printf("alarm signal\n");


}


 


void *pthread_func()


{


          alarm(2);


          while(1)


          {


                   pause();


          }


}


 


int main(int argc, char **argv)


{


          pthread_t tid;


          int retval;


 


          signal(SIGALRM, sig_handler);


         


          if((retval = pthread_create(&tid, NULL, pthread_func, NULL)) < 0)


          {


                   perror("pthread_create");


                   exit(-1);


          }


 


          while(1)


          {


                   printf("main thread\n");


                   sleep(10);


          }


          return 0;


}


这个程序的理想结果是:


main thread


alarm signal


alarm signal


alarm signal


alarm signal


alarm signal


main thread


可事实上并不是这样的,它的结果是:


main pthread


alarm signal


main pthread


alarm signal


main pthread


为什么会出现这种情况呢?是因为发送给工作线程的信号中断的主线程的sleep,并且这个中情况只影响主线程而不会影响到其他的工作线程。我们怎么才能解决这种问题呢,最简单的方法是修改这个程序,修改这个线程主线程使用alarm,工作线程使用sleep。这样就能够达到我们的要求,但是有时候有不能简单的这样操作。所以我们就需要进一步的修改我们的程序。在这里我第一个想到的是使用signal(SIGALRM, SIG_IGN),可是这个是设置整个进程对这个信号的响应方式,经过测试也确实不能完成我期望的功能,那么怎么办呢?有这样一个函数pthread_sigmask,线程中的信号屏蔽,函数的原型及相关函数为:


#include <signal.h>


int pthread_sigmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);


函数中第一个参数how有三个值SIG_BLOCK、SIG_SETMASK和SIG_UNBLOCK这里我们是用第二个值SIG_SETMASK


int sigemptyset(sigset_t *set);         /*清除信号集合set*/


int sigaddset(sigset_t *set, int signum); /*添加信号signum到信号集set中*/


然后我们改造我们的程序为:


#include <pthread.h>


#include <signal.h>


#include <stdio.h>


#include <stdlib.h>


#include <unistd.h>


#include <sys/time.h>


 


void sig_handler(int signo)


{


          alarm(2);


          printf("alarm signal\n");


}


 


void *pthread_func()


{


          alarm(2);


          while(1)


          {


                   pause();


          }


}


 


int main(int argc, char **argv)


{


          pthread_t tid, tid_1;


          int retval;


 


          signal(SIGALRM, sig_handler);


         


          if((retval = pthread_create(&tid, NULL, pthread_func, NULL)) < 0)


          {


                   perror("pthread_create");


                   exit(-1);


          }


         


          sigset_t sigset;


          sigemptyset(&sigset);


          sigaddset(&sigset, SIGALRM);


          pthread_sigmask(SIG_SETMASK,&sigset,NULL);


 


          while(1)


          {


                   printf("main pthread\n");


                   sleep(10);


          }


          return 0;


}


这个时候我们就能够看到我们想要的结果了。


这里再附一个setitimer的使用范例


#include <pthread.h>


#include <signal.h>


#include <stdio.h>


#include <stdlib.h>


#include <unistd.h>


#include <sys/time.h>


 


struct itimerval timerval;


void sig_handler(int signo)


{


          printf("alarm signal\n");


}


 


 


void *pthread_func()


{


 


          setitimer(ITIMER_REAL, &timerval, NULL);


          while(1)


          {


                   pause();


          }


}


 


int main(int argc, char **argv)


{


          pthread_t tid;


          int retval;


          timerval.it_interval.tv_sec = 2;


          timerval.it_interval.tv_usec = 0;


          timerval.it_value.tv_sec = 2;


          timerval.it_value.tv_usec = 0;


 


          signal(SIGALRM, sig_handler);


         


          if((retval = pthread_create(&tid, NULL, pthread_func, NULL)) < 0)


          {


                   perror("pthread_create");


                   exit(-1);


          }


 


          sigset_t sigset;


          sigemptyset(&sigset);


          sigaddset(&sigset, SIGALRM);


          pthread_sigmask(SIG_SETMASK,&sigset,NULL);


 


          while(1)


          {


                   printf("main thread\n");


                   sleep(5);


          }


          return 0;


}


 


个人的一些理解,有问题希望和大家交流!

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
4
关闭 站长推荐上一条 /6 下一条