定时器(容器+检测机制)
为什么要有定时器
- 精确控制任务执行时间
- 实现异步操作
- 任务调度和优先级管理
定时器有什么作用
- 组织管理大量延时任务的模块
- 不过度占用线程,高效处理定时任务
定时器怎么解决问题
通过组织大量延时任务的数据结构(容器)和触发最近将超时任务的机制
定时器应用
- 心跳机制
- 倒计时
- 延时任务
容器
对任务触发时间进行排序
- 红黑树 STL(map/set/multimap/multiset)
- 最小堆(priority_queue)
对执行顺序进行排序
- 时间轮 针对当前时间指针做偏移
触发机制
io多路复用的最后一个超时参数
eg:epoll_wait 第四个参数 超时参数
如果没有定时器,就会一直等待,直到有延迟任务的出现
timerfd (将定时器转化为io处理)
timerfd_create ( int clockid, int flags) ; 创建定时器文件描述符
clockid:
CLOCK_REALTIME 系统实时时钟,与系统时间同步,受用户手动修改系统时间影响
CLOCK_MONOTONIC 单调递增时钟,与系统启动开始计算,不受系统时间修改影响
flags:
TFD_NONBLOCK 非阻塞模式,若定时器未到期,read 立刻返回 EAGAIN 错误
timerfd_sertime(int fd, int flags, const struct itimerspec *new_value,struct itimerspec * old_value); 启动或停止定时器,设置超时时间和间隔
flags: 0 相对时间模式,new_value.it_value 表示相对于当前时间的延迟
TFD_TIMER_ABSTIME 绝对时间模式,new_value.it_value 表示具体时间点(如CLODK_REALTIME 的某个时间戳)
timerfd 使用:
1.ET模式
2.monotonic 时间参数
定时器优化思路
思考:
1.选择数据结构
- 触发时刻作为 key ,任务作为 val
- 快速找到最近要超时的任务 O(1)
- 触发后要删除该任务且支持随时删除任务,还要满足快速找到任务
- 允许相同时刻触发任务
因为有key-val,还要支持O(1)的查找,所以我们能想到 map,但是同一时刻可能会有多个任务,所以 我们使用 multimap作为容器。
2.设计任务
因为是异步执行延时任务,所以我们用回调函数,并使用函数对象
3.如何触发
这里我们使用epoll_wait 第四个参数 超时参数
性能优化
1.针对 不同功能或不同对象 设置定时器
2.针对多线程进行优化: 一个事件一个定时器, 或 单独时间轮线程
3.针对有规律的定时器任务(有大量相同时间间隔时间的延时任务),采用 emplace_hint接口
代码实现
class TimerNode {
public:
friend class Timer;
TimerNode(uint64_t timeout, std::function<void()> callback)
: timeout_(timeout), callback_(std::move(callback)) {}
private:
int id;
uint64_t timeout_;
std::function<void()> callback_;
};
class Timer {
public:
static Timer* GetInstance() {
static Timer instance;
return &instance;
}
static uint64_t GetCurrentTime() {
using namespace std::chrono;
return duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
}
TimerNode* AddTimeout(uint64_t diff, std::function<void()> cb) {
auto node = new TimerNode(GetCurrentTime() + diff, std::move(cb));
if (timer_map_.empty() || node->timeout_ < timer_map_.rbegin()->first) {
auto it = timer_map_.insert(std::make_pair(node->timeout_, std::move(node)));
return it->second;
} else {
auto it = timer_map_.emplace_hint(timer_map_.crbegin().base(), std::make_pair(node->timeout_, std::move(node)));
return it->second;
}
};
void DelTimeout(TimerNode* node) {
auto it = timer_map_.equal_range(node->timeout_);
for (auto iter = it.first; iter != it.second; ++iter) {
if (iter->second == node) {
timer_map_.erase(iter);
break;
}
}
}
//
int WaitTime() {
auto iter = timer_map_.begin();
if (iter == timer_map_.end()) {
return -1;
}
uint64_t diff = iter->first - GetCurrentTime();
return diff > 0 ? diff : 0;
}
void HandleTimeout() {
auto iter = timer_map_.begin();
while (iter != timer_map_.end() && iter->first <= GetCurrentTime()) {
iter->second->callback_();
iter = timer_map_.erase(iter); // 删除已处理的定时器
}
}
private:
std::multimap<uint64_t, TimerNode*> timer_map_;
Timer() = default;
Timer(const Timer&) = delete;
Timer& operator=(const Timer&) = delete;
Timer(Timer&&) = delete;
Timer& operator=(Timer&&) = delete;
~Timer() {
for (auto& pair : timer_map_) {
delete pair.second;
}
}
};
学习分享:https://github.com/0voice