定时器

目的

  • 处理某些延后处理事件
  • 常用于并发网络编程

实现方案

  • 数据结构:红黑树、时间轮、最小堆
  • 检测机制:timerfd(将定时器作为事件使用epoll)

定时器结点

struct TimerNodeBase {
time_t expire; // 触发时间;
uint64_t id;
};
struct TimerNode : public TimerNodeBase {
using Callback = std::function<void(const TimerNode &node)>;
TimerNode(uint64_t id, time_t expire, Callback func) : func(std::move(func)) {
this->expire = expire;
this->id = id;
}
Callback func;
};

定时器结构

class Timer {
public:
// 返回当前时间
static inline time_t GetTick() {
return std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch())
.count();
}
private:
static inline uint64_t GenGID() { return gid++; }
static uint64_t gid;
std::set<TimerNode, std::less<>> timeouts; // 内部使用红黑树
};

对外接口

  • AddTimeout:增加定时器设置超时时间和触发函数
  • DelTimeout:删除定时器
  • UpdateTimeout:使用timefd_settime检测是否有定时器到期
  • HandleTimer:执行到期任务,遍历所有符合条件的Timer
TimerNodeBase AddTimeout(int msec, TimerNode::Callback func) {
time_t expire = GetTick() + msec;
// TODO:某些情况下可以实现插入O(1),使用emplace_hint(iterator)
auto pairs = timeouts.emplace(GenGID(), expire, std::move(func));
return static_cast<TimerNodeBase>(*pairs.first);
}

void DelTimeout(TimerNodeBase &node) {
auto iter = timeouts.find(node); // c++14等效key
if (iter != timeouts.end()) {
timeouts.erase(iter);
}
}

// 检测到期任务
void UpdateTimerfd(const int fd) {
struct timespec abstime;
auto iter = timeouts.begin(); // 整个树中最小值
if (iter != timeouts.end()) {
abstime.tv_sec = iter->expire / 1000;
abstime.tv_nsec = (iter->expire % 1000) * 1000000;
} else {
abstime.tv_sec = 0;
abstime.tv_nsec = 0;
}
struct itimerspec its = {
.it_interval = {},
.it_value = abstime,
};

timerfd_settime(fd, TFD_TIMER_ABSTIME, &its, nullptr);
}

// 执行到期任务
void HandleTimer(time_t now) {
auto iter = timeouts.begin();
while (iter != timeouts.end() && iter->expire <= now) {
iter->func(*iter);
iter = timeouts.erase(iter);
}
}