http服务器_简易定时器的设计与C++实现(ref muduo)
一、定时器的意义
在网络程序中经常需要处理定时事件,比如定时检测一个客户端连接的活动状态,或者设定时间关闭客户端连接等。服务器程序通常管理着大量的定时器,如何高效的组织和管理这些定时器,使得其能在预期的时间点被触发,而且不影响服务器的主要逻辑,对于服务器的性能有很大的影响。
二、定时器管理的结构设计
传统的最简单的管理方式是采用按照时间排好序的线性表如排序链表,常用操作的复杂度为O(N)所以在管理大量的定时器时效率不高。
第二种方案是采用二叉堆组织的优先队列,这样常见操作的复杂度就可以降为O(logN)。而且还可以利用STL现有的容器priority_queue。
二、定时器的实现
//timer.h
#ifndef __TIMER_H__
#define __TIMER_H__
#include <iostream>
#include <cassert>
#include <functional>
#include <mutex>
#include <vector>
#include <queue>
#include <chrono>
namespace huhu{
using TimeoutCallBack = std::function<void()>;//回调函数
using Clock = std::chrono::high_resolution_clock;//拥有最小计次周期的时钟
using MS = std::chrono::milliseconds;
//time_point表示从格林威治标准时间(1970-01-01 00:00:00.000)开始到指定时间的计次周期数
using Timestamp = Clock::time_point;
//forward declaration
class HttpRequest;
class Timer {
public:
Timer(const Timestamp& when, const TimeoutCallBack& cb)
:m_expire_time(when),
m_timer_callback(cb),
m_delete(false){}
~Timer(){}
void del() {m_delete = true;}
bool isDeleted() {return m_delete;}
Timestamp getExpireTime() const {return m_expire_time;}
void runCallBack() {m_timer_callback();}
private:
Timestamp m_expire_time;
TimeoutCallBack m_timer_callback;
bool m_delete;
};// class Timer
struct cmp{
//functor
//仿函数,到期时间短的放前面
bool operator()(Timer* a, Timer* b){
assert(a != nullptr && b != nullptr);
return (a->getExpireTime()) > (b->getExpireTime());
}
};
class TimerManager{
public:
TimerManager()
:m_now(Clock::now()){}
~TimerManager(){}
void updateTime() {m_now = Clock::now();}
void addTimer(HttpRequest* request, const int& timeout, const TimeoutCallBack& cb);
void delTimer(HttpRequest* request);
void handleExpireTimer();//处理定时器事件
int getNextExpireTime();//获取下一个定时器超时的时间
private:
using TimerQueue = std::priority_queue<Timer*, std::vector<Timer*>, cmp>;//优先队列管理定时器
TimerQueue m_timer_queue;
Timestamp m_now;
std::mutex m_lock;
};//class TimerManager
}// namespace huhu
#endif //__TIMER_H__
//timer.cpp
#include "../include/Timer.h"
#include "../include/HttpRequest.h"
#include <cassert>
using namespace huhu;
void TimerManager::addTimer(HttpRequest* request, \
const int& timeout, \
const TimeoutCallBack& cb){
std::unique_lock<std::mutex> lock(m_lock);
assert(request != nullptr);
updateTime();
Timer* timer = new Timer(m_now + MS(timeout), cb);
m_timer_queue.push(timer);
// Call addTimer twice for the same request, you need to remove the previous timer
if(request->getTimer() != nullptr)
delTimer(request);
request->setTimer(timer);
}
//handleExpireTimers->runCallBack->__closeConnection->delTimer
void TimerManager::delTimer(HttpRequest* request){
assert(request != nullptr);
Timer* timer = request->getTimer();
if(timer == nullptr) return;
//lazy delete
timer->del();
request->setTimer(nullptr);
}
void TimerManager::handleExpireTimer(){
std::unique_lock<std::mutex> lock(m_lock);
updateTime();
while(!m_timer_queue.empty()) {
Timer* timer = m_timer_queue.top();
assert(timer != nullptr);
//是否已删除检测
if(timer->isDeleted()) {
m_timer_queue.pop();
if(timer != nullptr){
delete timer;
}
continue;
}
// check pri_queue front,if not timeout, return
if(std::chrono::duration_cast<MS>(timer->getExpireTime() - m_now).count() > 0) {
return;
}
// timeout
timer->runCallBack();
m_timer_queue.pop();
if(timer != nullptr){
delete timer;
}
}
}
int TimerManager::getNextExpireTime(){
std::unique_lock<std::mutex> lock(m_lock);
updateTime();
int res = -1;
while(!m_timer_queue.empty()) {
Timer* timer = m_timer_queue.top();
if(timer->isDeleted()) {
m_timer_queue.pop();
if(timer != nullptr){
delete timer;
}
continue;
}
res = std::chrono::duration_cast<MS>(timer->getExpireTime() - m_now).count();
res = (res < 0) ? 0 : res;
break;
}
return res;
}