(1)时间堆的原理
定时器以到期时间作为排序值,存放于最小堆这种数据结构中。时间堆不以固定的频率来查询是否有定时器到期,而是每次当堆顶的定时器到期后才处理一次定时事件,避免了定期查询导致的开销。
(2)难点分析及解决思路
Q:如何确定堆顶定时器是否到期?
A:仍然以定时的方式来提醒进程或线程有定时器到期,但定时时长是变化的,其值始终等于堆顶定时器到期的绝对时刻,与定时时刻之差;
Q:当插入一个新的定时器,或删除一个定时器后,堆顶定时器可能会发生变化,因此定时时长也就可能变化,如何及时更新定时时长呢?
A:借助epoll来监听,插入或删除定时器这一事件是否发生,一旦发生,则立即修改定时时长;
Q:如何实现定时呢?
A:可以使用alarm函数实现。但其实可以利用epoll_wait函数中的超时参数来进行定时。如果时间堆中无定时器,则超时参数为-1,促使其阻塞,直到有定时器插入事件发生;否则,超时参数等于定时时长。当定时器插入或删除事件发生后,epoll_wait函数返回,随即重新确定定时时长,并修改超时参数,以用于下一次的epoll_wait函数调用。
(3)时间堆框架
(4)源代码及注释
#ifndef TIME_HEAP
#define TIME_HEAP
#include <time.h>
#include <vector>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <stdio.h>
#include <pthread.h>
template <typename T> //T是客户类
class th_timer //定时器类
{
public:
time_t expire; //定时器到期的绝对时刻
T *user_data; //客户数据,应包含此定时器指针
void (*cb_func)(T*); //回调函数,即当定时器到期时,要执行的操作
int loc; //定时器在时间堆中的位置,从堆中删除定时器时会用到
public:
th_timer(time_t _expire, T *_user, void (*_cb_func)(T*))
:expire(_expire