linux系统下timer的用法详解
我们在写程序时,很多时候希望程序能够按照固定的周期执行。比较笨的办法是用一个while(1)循环,在循环里用一个sleep或者usleep函数延时,延时到一定时间执行需要执行的代码。这种方法忽略了程序代码运行的时间,所以程序循环的时间就不准了。为了能够活动比较准的定时时间,可以使用timer模块。timer模块的使用方法有2种比较常用的的用法,一种是产生新线程的...
我们在写程序时,很多时候希望程序能够按照固定的周期执行。比较笨的办法是用一个while(1)循环,在循环里用一个sleep或者usleep函数延时,延时到一定时间执行需要执行的代码。这种方法忽略了程序代码运行的时间,所以程序循环的时间就不准了。为了能够活动比较准的定时时间,可以使用timer模块。
timer模块的使用方法有2种比较常用的的用法,一种是产生新线程的方式,另一种是产生信号的方式。
1、timer产生新线程
这种使用方法,当timer定时结束后会产生一个新的线程,在新的线程中执行用户代码,执行完后退出线程。timer的初始化代码如下所示。
timerInit(int sec, int usec)
{
timer_t timerid;
struct sigevent evp;
memset(&evp, 0, sizeof(struct sigevent)); //清零初始化
evp.sigev_value.sival_int = 111; //标识定时器的
evp.sigev_notify = SIGEV_THREAD; //线程通知的方式,派驻新线程
evp.sigev_notify_function = timerHandle; //线程函数地址
if (timer_create(CLOCK_REALTIME, &evp, &timerid) == -1)
{
perror("fail to timer_create");
exit(-1);
}
struct itimerspec it;
it.it_interval.tv_sec = sec; //间隔s
it.it_interval.tv_nsec = usec*1000; //间隔ns
it.it_value.tv_sec = sec;
it.it_value.tv_nsec = usec*1000;
if (timer_settime(timerid, 0, &it, NULL) == -1)
{
perror("fail to timer_settime");
exit(-1);
}
}
timer的定时时间通过两个形参sec和usec传递,分别代表秒和微妙。程序中在主函数中调用timerInit函数时设置的定时周期为1秒。evp.sigev_notify = SIGEV_THREAD;将timer设置为派驻新线程的方式,evp.sigev_notify_function = timerHandle;这一句指定新线程的函数地址为timerHandle。也就是说,当1秒钟定时周期结束时,程序启动一个新线程,新线程转到timerHandle函数执行,在timerHandle函数中,执行的是代码命令,如下所示。
void timerHandle(union sigval v)
{
printf("Hello!\n");
}
采用下面的命令对程序进行编译
gcc timer_test.c -o timer_test -lrt
注意一定要加上 -lrt,否则编译不能通过。执行程序,则每隔一秒打印一次Hello!
2、timer产生信号
timer产生新线程的方式能够准确的产生定时周期,但也存在问题。如果周期执行的代码的执行时间超过定时周期,则会导致一个新起的线程没有执行完,又新起了一个新的线程。如果两个线程访问了相同的资源,比如一个很长的数组,就会导致冲突,可能会导致程序异常退出。
当然,如果每个周期代码执行过程都超时,那就要考虑优化代码或者修改定时周期的办法了。但如果只是某个周期超时,其他周期不超时,就不太好解决了。某个周期的超时就可能导致程序崩溃。
timer产生信号的方式可以有效解决上述问题,与产生新线程不同,产生信号的方式,不会产生新线程。当某个周期代码执行超时时,另一个计时信号产生时,信号会被阻塞,直到上个周期的代码执行完之后,再执行新的代码。
这种方式,timer初始化过程如下所示。
timerInit(int sec, int usec)
{
timer_t timerid;
struct sigevent evp;
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_handler = timerHandle;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
sigaction(SIGUSR1, &act, NULL);
memset(&evp, 0, sizeof(struct sigevent)); //清零初始化
evp.sigev_value.sival_int = 111; //标识定时器的
evp.sigev_signo = SIGUSR1;
evp.sigev_notify = SIGEV_SIGNAL; //产生信号
if (timer_create(CLOCK_REALTIME, &evp, &timerid) == -1)
{
perror("fail to timer_create");
exit(-1);
}
struct itimerspec it;
it.it_interval.tv_sec = sec; //间隔s
it.it_interval.tv_nsec = usec*1000; //间隔ns
it.it_value.tv_sec = sec;
it.it_value.tv_nsec = usec*1000;
if (timer_settime(timerid, 0, &it, NULL) == -1)
{
perror("fail to timer_settime");
exit(-1);
}
}
代码中,evp.sigev_notify = SIGEV_SIGNAL;将timer的工作方式设置为产生信号的方式,evp.sigev_signo = SIGUSR1;设置产生的信号为SIGUSR1。act.sa_handler = timerHandle;这一句将信号的处理函数设置为timerHandle,sigaction(SIGUSR1, &act, NULL);将SIGUSR1信号与timerHandle函数绑定在一起。当定时周期结束时,产生信号并触发timerHandle函数执行。如果上一个周期的代码没有执行完,则等待。
同样的方法编译代码,并执行。与上一个实例现象一样。
本文中的源码可以从本文的资源中下载。
更多推荐
所有评论(0)