Linux C/C++服务器
定时器的几种实现方案
定时器的应用
- 网络心跳检测
- 游戏技能冷却
- 倒计时
- 其他需要延时处理的功能
定时器的触发方式
- 网络事件和定时事件在一个线程处理
通常使用在定时任务比较少的场景,redis、nginx、memcached都是这样处理定时任务的,通常最多组织1024个定时器
// 网络事件和定时事件在一个线程中处理
while (!quit) {
int timeout = get_nearest_timer() - now();
if (timeout < 0) timeout = -1;
int nevent = epoll_wait(epfd, ev, nev, timeout);
for (int i=0; i<nevent; i++) {
// ... 处理网络事件
}
// 处理定时事件
update_timer();
}
- 网络事件和定时事件在不同的线程中处理
适用于定时任务比较多的场景,skynet等,通常使用时间轮数据结构进行实现(加锁力度比较小),定时器检测为单独的线程,执行通过信号或者插入执行队列让其他线程执行
// 网络事件和定时事件在不同线程中处理
void * thread_timer(void *thread_param) {
init_timer();
while (!quit) {
update_timer();
sleep(t);
}
clear_timer();
return NULL;
}
pthread_create(&pid, NULL, thread_timer, &thread_param);
定时器设计
接口设计
// 初始化定时器
void init_timer();
// 添加定时器
Node* add_timer(int expire, callback cb);
// 删除定时器
bool del_timer(Node* node);
// 找到最近要触发的定时任务
Node* find_nearest_timer();
// 更新检测定时器
void update_timer();
// 清除定时器
// void clear_timer();
数据结构设计
本质就是按照定时任务的优先级进行组织
- 按照触发时间顺序进行组织:红黑树(绝对有序)、最小堆(相对有序)、跳表(绝对有序)
- 按照执行顺序进行组织:时间轮