最近项目中需要用到c++中定时器来启动某些预定义好的函数,达到某些目的因此着重对c++中相关定时器进行了学习,现将其记录如下:
一、具体步骤
一个定时器从创建、初始化、到删除一共分为三个不同的函数:
(1)、timer_create()----创建定时器
int timer_create(clockid_t clock_id,struct sigevent *evp, timer_t * timerid)
代码中可以通过调用timer_create创建定时器,每个进程拥有各自单独的定时器,并不是在fork时继承的。clock_id说明了定时器属于哪个时钟;* timerid装载的是被创建的定时器ID;该函数创建了定时器,并将其ID放入timerid指向的位置中。
evp制定了定时器到期要产生的异步通知,如果为NULL,则定时器到期将会产生默认的信号,对于CLOCK_REALTIME来说,默认信号就是SIGALRM;如果产生除默认信号之外的其他信号,则必须将evp->sigen_signo设置为期望的信号码。struct sigevent结构中的成员evp->sigev_notify说明了定时器到期时应采取的行动。通常,该成员变量的值为SIGEV_SIGNAL,说明该定时器到期时,会产生一个信号。编程者可将evp->sigev_notify设置成为SIGEV_NONE来防止定时器到期时产生信号。
该函数其形参中的结构体的介绍如下:
clock_id说明了定时器属于哪个时钟,取值如下:
CLOCK_REALTIME: Systemwide realtime clock
CLOCK_MONOTONIC: Represents monotonic time
CLOCK_PROCESS_CPUTIME_ID: High resolution per-process timer.
CLOCK_THREAD_CPUTIME_ID:Thread-specific timer
CLOCK_REALTIME_HR:high resolution version of CLOCK_REALTIME
CLOCK_MONOTONIC_HR:High resolution version of CLOCK_MONOTONIC
struct sigevent 制定了定时器到期要产生的异步通知
{
int sigev_notify; //notification type
int sigev_signo; //signal number
union sigval sigev_value; //signal value
void (*sigev_notify_function)(union sigval);
pthread_attr_t *sigev_notify_attributes;
}
union sigval
{
int sival_int; //integer value
void *sival_ptr; //pointer value
}
通过将evp->sigev_notify设定为如下值来定制定时器到期后的行为:
SIGEV_NONE:什么都不做,只提供通过timer_gettime和timer_getoverrun查询超时信息。
SIGEV_SIGNAL: 当定时器到期,内核会将sigev_signo所指定的信号传送给进程。在信号处理程序中,si_value会被设定会sigev_value。
SIGEV_THREAD: 当定时器到期,内核会(在此进程内)以sigev_notification_attributes为线程属性创建一个线程,并且让它执行sigev_notify_function,传入sigev_value作为为一个参数。
启动一个定时器:
timer_create()所创建的定时器并未启动。要将它关联到一个到期时间以及启动时钟周期,可以使用timer_settime()。
int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, struct itimerspect *ovalue);
struct itimespec{
struct timespec it_interval;
struct timespec it_value;
};
如同settimer(),it_value用于指定当前的定时器到期时间。当定时器到期,it_value的值会被更新成it_interval 的值。如果it_interval的值为0,则定时器不是一个时间间隔定时器,一旦it_value到期就会回到未启动状态。timespec的结构提供了纳秒级分辨率:
struct timespec{
time_t tv_sec;
long tv_nsec;
};
如果flags的值为TIMER_ABSTIME,则value所指定的时间值会被解读成绝对值(此值的默认的解读方式为相对于当前的时间)。这个经修改的行为可避免取得当前时间、计算“该时间”与“所期望的未来时间”的相对差额以及启动定时器期间造成竞争条件。
如果ovalue的值不是NULL,则之前的定时器到期时间会被存入其所提供的itimerspec。如果定时器之前处在未启动状态,则此结构的成员全都会被设定成0。
获得一个活动定时器的剩余时间:
int timer_gettime(timer_t timerid,struct itimerspec *value);
取得一个定时器的超限运行次数:
有可能一个定时器到期了,而同一定时器上一次到期时产生的信号还处于挂起状态。在这种情况下,其中的一个信号可能会丢失。这就是定时器超限。程序可以通过调用timer_getoverrun来确定一个特定的定时器出现这种超限的次数。定时器超限只能发生在同一个定时器产生的信号上。由多个定时器,甚至是那些使用相同的时钟和信号的定时器,所产生的信号都会排队而不会丢失。
int timer_getoverrun(timer_t timerid);
执行成功时,timer_getoverrun()会返回定时器初次到期与通知进程(例如通过信号)定时器已到期之间额外发生的定时器到期次数。举例来说,在我们之前的例子中,一个1ms的定时器运行了10ms,则此调用会返回9。如果超限运行的次数等于或大于DELAYTIMER_MAX,则此调用会返回DELAYTIMER_MAX。
执行失败时,此函数会返回-1并将errno设定会EINVAL,这个唯一的错误情况代表timerid指定了无效的定时器。
删除一个定时器:
int timer_delete (timer_t timerid);
一次成功的timer_delete()调用会销毁关联到timerid的定时器并且返回0。执行失败时,此调用会返回-1并将errno设定会 EINVAL,这个唯一的错误情况代表timerid不是一个有效的定时器。
例1:
void handle()
{
time_t t;
char p[32];
time(&t);
strftime(p, sizeof§, “%T”, localtime(&t));
printf(“time is %s/n”, p);
}
int main()
{
struct sigevent evp;
struct itimerspec ts;
timer_t timer;
int ret;
evp.sigev_value.sival_ptr = &timer;
evp.sigev_notify = SIGEV_SIGNAL;
evp.sigev_signo = SIGUSR1;
signal(SIGUSR1, handle);
ret = timer_create(CLOCK_REALTIME, &evp, &timer);
if( ret )
perror(“timer_create”);
ts.it_interval.tv_sec = 1;
ts.it_interval.tv_nsec = 0;
ts.it_value.tv_sec = 3;
ts.it_value.tv_nsec = 0;
ret = timer_settime(timer, 0, &ts, NULL);
if( ret )
perror(“timer_settime”);
while(1);
}
例2:
void handle(union sigval v)
{
time_t t;
char p[32];
time(&t);
strftime(p, sizeof§, “%T”, localtime(&t));
printf("%s thread %lu, val = %d, signal captured./n", p, pthread_self(), v.sival_int);
return;
}
int main()
{
struct sigevent evp;
struct itimerspec ts;
timer_t timer;
int ret;
memset (&evp, 0, sizeof (evp));
evp.sigev_value.sival_ptr = &timer;
evp.sigev_notify = SIGEV_THREAD;
evp.sigev_notify_function = handle;
evp.sigev_value.sival_int = 3; //作为handle()的参数
ret = timer_create(CLOCK_REALTIME, &evp, &timer);
if( ret)
perror(“timer_create”);
ts.it_interval.tv_sec = 1;
ts.it_interval.tv_nsec = 0;
ts.it_value.tv_sec = 3;
ts.it_value.tv_nsec = 0;
ret = timer_settime(timer, TIMER_ABSTIME, &ts, NULL);
if( ret )
perror(“timer_settime”);
while(1);
}