定时器的使用主要涉及到以下3个函数:
#include <time.h>
timer_create();
timer_settime();
timer_delete();
1. 创建定时器
int timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid);
参数说明:
- clock_id: 指定定时器的时钟类型,可以是CLOCK_REALTIME或CLOCK_MONOTONIC
- evp: 指向sigevent结构体的指针,用来指定定时器到期时的通知方式
- timerid: 指向用来保存定时器指针,可以理解为就是创建的定时器对象
- 返回值:0表示创建成功,其他表示创建失败
1.1 clock_id取值
clock_id的取值如下:
- CLOCK_REALTIME :Systemwide realtime clock.
- CLOCK_MONOTONIC:Represents monotonic time. Cannot be set.
- 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.
1.2 struct sigevent结构体说明
typedef struct sigevent {
__sigval_t sigev_value;
int sigev_signo;
int sigev_notify;
union {
int _pad[__SIGEV_PAD_SIZE];
__pid_t _tid;
struct {
void (*_function) (__sigval_t); /* Function to start. */
pthread_attr_t *_attribute; /* Thread attributes. */
} _sigev_thread;
} _sigev_un;
} sigevent_t;
#define sigev_notify_function _sigev_un._sigev_thread._function
#define sigev_notify_attributes _sigev_un._sigev_thread._attribute
......
union sigval {
int sival_int;
void *sival_ptr;
};
typedef union sigval __sigval_t;
对其中的参数说明如下:
- sigev_value 表示该信号传递的值,可以是一个整数或者一个指针
- sigev_signo 指定信号的值
- sigev_notify表示定时器到期后信号的通知方式,可以是以下几种:
- SIGEV_NONE:什么都不做,只提供通过timer_gettime和timer_getoverrun查询超时信息。
- SIGEV_SIGNAL: 当定时器到期,内核会将sigev_signo所指定的信号传送给进程。
- SIGEV_THREAD: 当定时器到期,内核会(在此进程内)以sigev_notification_attributes为线程属性创建一个线程,并且让它执行sigev_notify_function,传入sigev_value作为为一个参数。
- SIGEV_THREAD_ID:将信号传递给指定的线程(不通用,Linux平台特有的)
- _tid 指定接收信号的线程id
- sigev_notify_function 定时器到期后的回调函数(配合SIGEV_THREAD通知使用)
- sigev_notify_attributes 定时器到期后创建线程的属性(配合SIGEV_THREAD通知使用)
1.3 创建定时器举例
static void timer_callback_function(union sigval sigval)
{
// @TODO
}
void demo_create_timer()
{
timer_t timer_id;
struct sigevent s_event;
s_event.sigev_notify = SIGEV_THREAD;
s_event.sigev_notify_function = timer_callback_function; // 设置到期后的回调函数
s_event.sigev_value.sival_int = index; // 设置需要传递的值,如果无需传递值也可以不写
s_event.sigev_notify_attributes = NULL;
int result = timer_create(CLOCK_REALTIME, &s_event, &timer_id);
if (result) {
LOG_ERROR("timer create error is %s", strerror(errno));
return;
}
}
以上创建了一个定时器,该定时器到期后,会创建一个线程运行timer_callback_function
回调函数。
2. 启动定时器
当定时器创建后是没有运行的,需要使用timer_settime
指定运行参数后启动。
timer_settime
的原型和使用的的时间相关结构体如下:
int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, struct itimerspect *ovalue);
struct itimespec{
struct timespec it_interval; // 定时器重复时间,如果这个结构体的值设置为0表示只执行1次,否则会按照这个设定的时间不断执行
struct timespec it_value; // 定时器的超时时间,当这个结构体设置为0时,表示关闭这个定时器
};
struct timespec{
time_t tv_sec; // 秒
long tv_nsec; // 纳秒
};
函数的一些参数说明:
- timerid:定时器 ID,可以使用 timer_create() 函数创建。
- flags:控制定时器行为的标志。目前只有一个选项 0,表示不需要特殊行为。
- new_value:一个指向 struct itimerspec 结构的指针,用于设置定时器的新值。
- old_value:一个指向 struct itimerspec 结构的指针,用于存储设置之前定时器的旧值。可以为 NULL,表示不需要保存旧值。
- 返回0表示成功,返回其他表示失败。
接上面的demo程序,当需要启动一个定时器是:
void demo_create_timer()
{
timer_t timer_id;
struct itimerspec ts;
......
// 设定运行时间
ts.it_value.tv_sec = 1;
ts.it_value.tv_nsec = 0;
ts.it_interval.tv_sec = 1;
ts.it_interval.tv_nsec = 0;
timer_settime(timerid, 0, &timer_value, NULL);
}
以上代码会让定时器按1秒的周期调用一次回调函数。
3. 删除定时器
删除定时器的函数原型如下:
int timer_delete (timer_t timerid);
- timerid 待删除的定时器
- 返回0表示删除成功,否则表示删除失败
4. 其他定时器函数
int timer_gettime(timer_t timerid, struct itimerspec *curr_value); // 获得定时器的到期时间和间隔
int timer_getoverrun(timer_t timerid); // 获得定时器超限的次数
5. SIGEV_THREAD_ID 通知方式的使用
示例代码:
timer_t *timer_id;
/** 线程函数 */
static void *timer_thread_function(void *data)
{
struct sigevent s_event;
s_event.sigev_notify = SIGEV_THREAD_ID;
s_event._sigev_un._tid = gettid(); // 获取当前线程的 TID
s_event.sigev_signo = 50; // 指定信号值,如果无特殊需求也可以不指定,默认是14 (SIGALRM)
timer_create(CLOCK_REALTIME, &s_event, &timer_id);
sigset_t set; // 指定当前线程能够接收的信号
// 以下设置根据需要进行选择
sigfillset(&set); // 接收所有的信号
// sigemptyset(&set); // 清空,不接收所有的信号
// sigaddset(&set, 50); // 接收指定信号50
pthread_sigmask(SIG_SETMASK, &set, NULL); // 让上面设定接收的信号生效
while (true) {
sigwait(&set, &sig); // 等待定时器超时后的信号
// @TODO
}
}
void demo_create_timer()
{
// 创建线程
pthread_t timer_callback_thread
pthread_create(&timer_callback_thread, NULL,timer_thread_function, NULL);
}
完成上面的定时器创建后,调用timer_settime
启动定时器,当定时器超时后,即可回调到创建的线程中。