基于STL的小根堆定时器实现(C++)
详细内容参见:《Linux高性能服务器编程》第11章 定时器。
下面谈的是个人理解,观点不一定对。
小根堆定时器的优点如下:
1. 添加时间复杂度为O(1);
2. 删除时间复杂度为O(1);
3.执行一个定时器的时间复杂度为O(1);
4.比起传统的按照固定时间间隔执行tick函数的链表/时间轮定时器,小根堆定时器可以动态地将所有定时器时间的最小的一个定时器的超时值作为心博时间。这样,一旦心博函数tick()被调用,则超时时间最小的定时器一定到期,执行相关处理。然后将下一个最小值定时器作为下一次的超时时间。这样,减少了调用tick()的次数。
由此可见,最小堆定时器相比链表定时器和时间轮定时器的效率会更好些。
网上大多是关于时间堆/最小堆定时器的实现都是手写实现堆的增删改等操作,比较麻烦。因为C++的内部已经有优先队列,直接调用相关函数就可以构建堆、增加、修改等操作,函数如下:
#include<algorithm>
using namespace std;
make_heap(array.begin(), array.end()); //构建堆
push_heap(array.begin(), array.end()); // 插入元素
pop_heap(array.begin(), array.end()); // 弹出元素
sort_heap(array.begin(), array.end()); // 排序
因此我没有再把堆的操作那一套函数自己手动实现了,直接使用algorithm的内置函数实现小根堆定时器。原书中是手动实现了一个堆。
代码
- heap_timer.h
//
// Created by yongpu on 2019/12/16.
//
#ifndef HEAP_TIMER_HEAP_TIMER_H
#define HEAP_TIMER_HEAP_TIMER_H
#include <iostream>
#include <netinet/in.h>
#include <time.h>
#include <vector>
#include <algorithm>
using namespace std;
#define BUFFER_SIZE 64
struct client_data;
/* 定时器类 */
class heap_timer_node {
public:
heap_timer_node(int delay) {
// expire = time(NULL) + delay;
expire = delay;
validFlag = true;
}
public:
time_t expire; /* 定时器生效的绝对时间 */
void (*cb_func)(client_data *); /* 定时器的回调函数 */
client_data *user_data; /* 用户数据 */
bool validFlag; /* 有效标志 */
};
/* 构建堆时的比较函数 */
bool cmp_heap_timer_node(heap_timer_node *node1, heap_timer_node *node2);
/* 时间堆类 */
class timer_heap {
public:
timer_heap(int cap);
void add_timer(heap_timer_node *timer); /* 添加目标定时器timer */
void del_timer(heap_timer_node *timer); /* 删除目标定时器timer */
heap_timer_node *get_top(); /* 获得堆顶部的定时器 */
void del_top(); /* 删除堆顶部的定时器 */
void tick(); /* 心博函数 */
void disPlay(); /* 输出堆中所有定时器 */
bool is_empty(); /* 堆是否为空 */
~timer_heap(); /* 销毁时间堆 */
private:
void resize(); /* 将当前容量扩大一倍 */
private:
vector<heap_timer_node *> array; /* 堆数组 */
int capacity; /* 堆数组的容量 */
int cur_size; /* 堆数组中当前的元素个数 */
int deleteNum; /* 已删除的定时器数量 */
};
/* 绑定socket和定时器 */
struct client_data {
sockaddr_in address;
int sockfd;
char buf[BUFFER_SIZE];
heap_timer_node *timer;
};
#endif //HEAP_TIMER_HEAP_TIMER_H
- heap_timer.cpp
//
// Created by yongpu on 2019/12/16.
//
#include "heap_timer.h"
/* 用于构造堆 */
bool cmp_heap_timer_node(heap_timer_node *node1, heap_timer_node *node2) {
return node1->expire > node2->expire;
}
/* 构造函数 */
timer_heap::timer_heap(int cap) {
this->capacity = cap;
this->array.resize(cap);
this->cur_size = 0;
this->deleteNum = 0;
for (int i = 0; i < cap; i++) {
this->array[i] = nullptr;
}
make_heap(array.begin(), array.begin() + cur_size, cmp_heap_timer_node);
}
/* 添加一个定时器 */
void timer_heap::add_timer(heap_timer_node *timer) {
if (timer == nullptr) {
return;
}
if (cur_size >= capacity) { /* 无空间,则将array扩大一倍 */
resize();
}
/* 插入新元素 */
array[cur_size] = timer;
cur_size++;
/* 调整堆 */
push_heap(array.begin(), array.begin() + cur_size, cmp_heap_timer_node);
}
/* 删除一个定时器 */
void timer_heap::del_timer(heap_timer_node *timer) {
if (timer == nullptr) {
return;
}
/* 仅仅将目标定时器的回调函数设置为空,即所谓的延迟销毁,
* 将节省真正删除该定时器造成的开销,但这样做容易造成堆数组膨胀 */
timer->cb_func = nullptr;
timer->validFlag = false; /* 将定时器标记为无效 */
deleteNum++;
/* 判断删除数是否达到当前容量的一半,达到了则删除并重新建堆 */
if (deleteNum >= cur_size / 2) {
int vaildIndex = 0;
for (int i = 0; i < cur_size; i++) {
if (array[i]->validFlag) {
array[vaildIndex] = array[i];
vaildIndex++;
array[i] = nullptr;
} else {
delete array[i];
array[i] = nullptr;
}
}
cur_size = vaildIndex;
make_heap(array.begin(), array.begin() + cur_size, cmp_heap_timer_node);
}
}
/* 获取堆顶定时器 */
heap_timer_node *timer_heap::get_top() {
if (is_empty()) {
return nullptr;
} else {
return array.front();
}
}
/* 删除堆顶定时器 */
void timer_heap::del_top() {
/* pop_heap 将堆顶元素移至当前堆的最末尾位置,及array[array.begin() + cur_size-1]处*/
pop_heap(array.begin(), array.begin() + cur_size, cmp_heap_timer_node);
/* 删除该定时器 */
delete array[cur_size - 1];
array[cur_size - 1] = nullptr;
cur_size--;
}
/* 心博函数 */
void timer_heap::tick() {
heap_timer_node *topTimer = get_top();
time_t cur = time(nullptr);
while (!is_empty()) {
if (topTimer == nullptr) {
break;
}
/* 如果对顶元素还没有到期,则退出循环 */
if (topTimer->expire > cur) {
break;
}
/* 否则执行堆顶定时器中的任务 */
if (topTimer->validFlag && topTimer->cb_func != nullptr) {
topTimer->cb_func(topTimer->user_data);
}
/* 删除堆顶元素 */
del_top();
topTimer = get_top();
}
}
/* 将array的容量扩大一倍 */
void timer_heap::resize() {
array.resize(capacity * 2);
capacity = capacity * 2;
}
void timer_heap::disPlay() {
cout << "disPlay: ";
for (int i = 0; i < capacity; i++) {
if (array[i] == nullptr) {
break;
}
cout << array[i]->expire << " ";
}
cout << endl;
}
/* 判断堆是否为空 */
bool timer_heap::is_empty() {
return cur_size == 0;
}
/* 析构函数,销毁堆 */
timer_heap::~timer_heap() {
for (int i = 0; i < cur_size; i++) {
delete array[i];
array[i] = nullptr;
}
}
- main.cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include "heap_timer.h"
using namespace std;
int main() {
heap_timer_node *node1 = new heap_timer_node(6);
heap_timer_node *node2 = new heap_timer_node(2);
heap_timer_node *node3 = new heap_timer_node(4);
heap_timer_node *node4 = new heap_timer_node(11);
heap_timer_node *node5 = new heap_timer_node(5);
timer_heap heaps(10);
heaps.add_timer(node1);
heaps.add_timer(node2);
heaps.add_timer(node3);
heaps.add_timer(node4);
heaps.add_timer(node5);
heaps.get_top();
heaps.disPlay();
heaps.del_timer(node1);
heaps.del_timer(node2);
heaps.del_timer(node3);
heaps.disPlay();
return 0;
}
- 运行