设计
classDiagram
class Timer {
<<enable_shared_from_this>>
-uint64_t m_ms
-uint64_t m_next
-std::function<void()> m_cb
-bool m_recurring
-TimerManager* m_manager
+bool cancel()
+bool refresh()
+bool reset(uint64_t ms, bool from_now)
-Timer(uint64_t ms, std::function<void()> cb, bool recurring, TimerManager* manager)
-Timer(uint64_t next)
+struct Comparator
}
class TimerManager {
-RWMutexType m_mutex
-std::set<Timer::ptr, Timer::Comparator> m_timers
-bool m_tickled
-uint64_t m_previouseTime
+TimerManager()
+~TimerManager()
+Timer::ptr addTimer(uint64_t ms, std::function<void()> cb, bool recurring = false)
+Timer::ptr addConditionTimer(uint64_t ms, std::function<void()> cb, std::weak_ptr<void> weak_cond, bool recurring = false)
+uint64_t getNextTimer()
+void listExpiredCb(std::vector<std::function<void()>>& cbs)
+bool hasTimer()
+bool detectClockRollover(uint64_t now_ms)
#virtual void onTimerInsertedAtFront() = 0
#void addTimer(Timer::ptr val, RWMutexType::WriteLock& lock)
}
Timer --|> enable_shared_from_this
Timer "1" -- "1" TimerManager : belongs to
TimerManager o-- "many" Timer : manages
Timer 和 TimerManger 互为 友元类,Timer 持有 TimerManager* 成员变量⭐⭐⭐⭐⭐ 好处: Timer可以自己管理自身,通过Timer实例 cancel,refresh,reset。(无需通过TimerManager操作)
红黑树 m_timers
std::set<Timer::ptr, Timer::Comparator> m_timers;
calss Timer{
/**
* 定时器比较仿函数,最小堆
*/
struct Comparator{
bool operator()(const Timer::ptr& lhs, const Timer::ptr& rhs) const{
if(!lhs && !rhs){
return false;
}
// 空的放前面???
if(!lhs){
return true;
}
if(!rhs){
return false;
}
if(lhs->m_next < rhs->m_next){
return true;
}
if(lhs->m_next > rhs->m_next){
return false;
}
return lhs.get() < rhs.get();
}
};
};
bool Timer::refresh(); // 刷新设置定时器的执行时间,按照当前时间,定时周期不变 bool Timer::reset(uint64_t ms, bool from_now); // 重置定时器时间
添加定时器 ⭐⭐⭐⭐⭐
Timer::ptr TimerManager::addTimer(uint64_t ms, std::function<void()> cb, bool recurring){
Timer::ptr timer(new Timer(ms, cb, recurring, this));
RWMutexType::WriteLock lock(m_mutex);
addTimer(timer, lock);
return timer;
}
// 增加代码复用,让addConditionTimer 可以调用 addTiemr ⭐
// 若直接使用std::shared_ptr参数,
// 当定时器回调持有该shared_ptr时,会强制延长关联对象的生命周期,即使外部已不再需要该对象。⭐⭐⭐
// 用weak_ptr则不会增加引用计数,允许关联对象在外部引用归零时正常析构。⭐
// 这种模式常用于需要对象关联生命周期的定时任务,例如:
// 网络连接超时检测(当连接已关闭时无需触发超时回调)
// 资源释放校验(当资源持有者已销毁时取消清理操作)
static void OnTimer(std::weak_ptr<void> weak_ptr, std::function<void()> cb){
std::shared_ptr<void> it = weak_ptr.lock();
if(it){
cb();
}
}
Timer::ptr TimerManager::addConditionTimer(uint64_t ms, std::function<void()> cb, std::weak_ptr<void> weak_cond, bool recurring){
return addTimer(ms, std::bind(&OnTimer, weak_cond, cb), recurring);
}
uint64_t TimerManager::getNextTimer(){
RWMutexType::ReadLock lock(m_mutex);
// 获取下一次最近执行事件的 相对事件,重置 m_tickle
m_tickled = false;
if(m_timers.empty()){
return ~0ull;
}
const Timer::ptr& next = *m_timers.begin();
uint64_t now_ms = GetCurrentTimeMS();
if(now_ms >= next->m_next){
return 0;
} else {
return next->m_next - now_ms;
}
}
// 获取所有超时事件
void TimerManager::listExpiredCb(std::vector<std::function<void()> >& cbs){
uint64_t now_ms = GetCurrentTimeMS();
std::vector<Timer::ptr> expired;
{
RWMutexType::ReadLock lock(m_mutex);
if(m_timers.empty()){
return;
}
}
RWMutexType::WriteLock lock(m_mutex);
bool rollover = detectClockRollover(now_ms);
if(!rollover && ((*m_timers.begin())->m_next > now_ms)){
return;
}
Timer::ptr now_timer(new Timer(now_ms));
// 获取到第一个 发生时间 < 现在。即需要执行的事件
// 如果系统时间被往前调整了 1个小时,就把全部定时器的事件 返回。
// 这个就比较粗暴了~
auto it = rollover ? m_timers.end() : m_timers.lower_bound(now_timer);
while(it != m_timers.end() && (*it)->m_next == now_ms){
++it;
}
expired.insert(expired.begin(), m_timers.begin(), it);
m_timers.erase(m_timers.begin(), it);
cbs.reserve(expired.size());
for(auto& timer : expired){
cbs.push_back(timer->m_cb);
// 如果事件需要重复执行,再次写回 m_timers ⭐
if(timer->m_recurring){
timer->m_next = now_ms + timer->m_ms;
m_timers.insert(timer);
}else{
timer->m_cb = nullptr;
}
}
}
// 添加 Timer,需要判断是否在堆顶。
// 如果在堆顶,此时需要onTimerInsertedAtFront(),唤醒 epoll_wait
void TimerManager::addTimer(Timer::ptr val, RWMutexType::WriteLock& lock){
auto it = m_timers.insert(val).first;
bool at_front = (it == m_timers.begin()) && !m_tickled;
if(at_front){
m_tickled = true;
}
lock.unlock();
if(at_front){
onTimerInsertedAtFront();
}
}
bool TimerManager::hasTimer(){
RWMutexType::ReadLock lock(m_mutex);
return !m_timers.empty();
}
bool TimerManager::detectClockRollover(uint64_t now_ms){
bool rollover = false;
if(now_ms < m_previouseTime && now_ms < (m_previouseTime - 60 * 60 * 1000)){
rollover = true;
}
m_previouseTime = now_ms;
return rollover;
}
IOManager的调整
void IOManager::onTimerInsertedAtFront(){
SYLAR_LOG_DEBUG(g_logger) << "onTimerInsertedAtFront() called";
tickle();
}
bool IOManager::stopping(){
uint64_t timeout = 0;
return stopping(timeout);
}
bool IOManager::stopping(uint64_t& next_timeout){
next_timeout = getNextTimer();
return next_timeout== ~0ull && m_pendingEventCount == 0 && Scheduler::stopping();
}
void IOManager::idle(){
SYLAR_LOG_DEBUG(g_logger) << "IOManager::idle() started";
// 一次epoll_wait最多检测 256 个就绪事件
const uint64_t MAX_EVENTS = 256;
epoll_event* events = new epoll_event[MAX_EVENTS]();
// 通过shared_ptr管理,避免内存泄漏 ⭐
std::shared_ptr<epoll_event> shared_events(events, [](epoll_event* ev){
delete[] ev;
});
while(true){
// 获取下一个定时器的超时事件,顺便判断调度器是否停止。
uint64_t next_timeout = 0;
if(stopping(next_timeout)){
SYLAR_LOG_DEBUG(g_logger) << "IOManager::idle() stopping, name=" << getName();
break;
}
int rt = 0;
do{
static const int MAX_TIMEOUT = 5000;
if(next_timeout != ~0ull){
next_timeout = std::min((int)next_timeout, MAX_TIMEOUT); ⭐⭐⭐
}else{
next_timeout = MAX_TIMEOUT;
}
SYLAR_LOG_DEBUG(g_logger) << "epoll_wait with timeout=" << next_timeout;
rt = epoll_wait(m_epfd, events, MAX_EVENTS, (int)next_timeout);
if(rt < 0){
if(errno == EINTR){ //在任何请求的事件发生或超时到期之前,信号处理程序中断了该调用
SYLAR_LOG_WARN(g_logger) << "epoll_wait interrupted by signal, retrying";
continue;
}
SYLAR_LOG_ERROR(g_logger) << "epoll_wait error, errno=" << errno << ", errstr=" << strerror(errno);
break;
}else{
break;
}
}while(true);
// 退出epoll_wait
std::vector<std::function<void()>> cbs;
listExpiredCb(cbs); ⭐
if(!cbs.empty()){
for(const auto& cb: cbs){
schedule(cb); // ⭐重新添加到调度队列,不直接调度
}
cbs.clear();
}
// 处理 pipe
// 处理 事件,将对应事件的任务添加到调度队列
...
} // end while(true)
SYLAR_LOG_DEBUG(g_logger) << "IOManager::idle() exited";
}
知识点
定时器实现方式
- 当前项目使用 epoll_wait 实现等待,查询 set 红黑树,将任务添加到调度器
- libgo 通过 std::condition_variable 定时唤醒,查询跳表,将任务添加到调度器
- 时间轮