已经好几天没有写博客了。
这几天都在看定时器,想搞清楚他的原理,想知道他到底是怎么用的。无奈没有一篇能够比较系统地告诉我定时器是干什么的,怎么用的,导致项目长时间停滞。
在看了几篇博客之后,根据我自己的理解,实现了一个简单的定时器,其中有参考其他的博文的写法,好在我现在实现了一个看起来能用的定时器。
定时器的应用场景
场景一:keep alive保活机制
成千上万个客户端去连接一台聊天服务器,那么就会存在成千上万个tcp连接。但是这些tcp连接是每时每刻都保持发包收包的活跃状态吗?不是!
某些tcp连接上,可能建立之后,在一天之内就没再发包/收包过,为了把有限的系统资源分配给更活跃的用户使用,我们应该设计一种方案来踢掉空闲连接。
场景二:游戏中,指定时间(或间隔时间)执行某种特定操作
-
每日/每周/每月,特定时间执行一次操作
-
循环执行的定时器,比如每隔一分钟刷新一次野怪
-
只执行一次的定时器,在60秒后定时器超期,执行操作A
各种定时器性能比较
实现方式 | StartTimer | StopTimer | PerTickBookkeeping |
---|---|---|---|
基于排序的链表 | O(n) | O(1) | O(1) |
基于最小堆 | O(logn) | O(1) | O(1) |
基于时间轮 | O(1) | O(1) | O(1) |
具体实现
这里采用了最小堆实现
Timer_mng.h
#ifndef __TIMER_MNG_
#define __TIMER_MNG_
#define ull unsigned long long
#define __INF__ 1e18
using namespace std;
enum t_type {ONCE,CIRCLE};
class Timer_mng;
class Timer;
struct Heap_entry{ //堆条目
unsigned long long time;
Timer *timer;
};
struct Timer{
t_type ttype; //任务类型,一次还是循环
void *(*run)(void *args); //回调函数
void *arg; //会调函数参数
ull itvl; //时间片长度(ms)
ull expires; //到期时间
int heapIndex; //在堆中的下标
Timer(); //初始化
void Start(void *(*run)(void *args),void *arg,ull itvl, t_type ttype);
void OnTimer(); //无剩余时间
};
struct Timer_mng{
vector<Heap_entry*>heap; //堆
~Timer_mng(); //析构函数
void DetectTimers(); //检测是否超时
void AddTimer(Timer* timer); //向堆中添加一个定时器
void RemoveTimer(Timer* timer); //移除定时器
void UpHeap(int idx); //堆操作,向上更新
void DownHeap(int idx); //堆操作,向下更新
void SwapHeap(int idx1, int idx2); //更换堆中的两个元素
};
ull Get_now(); //得到当前时间(ms)
#endif
Timer_mng.cpp
#include<cstdio>
#include<cstring>
#include<sys/time.h>
#include<vector>
#include"Timer_mng.h"
using namespace std;
//Timer
Timer::Timer(){
heapIndex=-1;
}
void Timer::Start(void *(*run)(void *args),void *arg,ull itvl, t_type ttype){
itvl=itvl;
this->run=run,this->arg=arg;
ttype=ttype;
printf("Now :%llu\n",Get_now());
expires=itvl+Get_now();
}
void Timer::OnTimer(){
heapIndex=-1;
// printf("%d\n",*(int*)arg);
this->run(this->arg);
}
//Timer_mng
Timer_mng::~Timer_mng(){
heap.clear();
}
void Timer_mng::AddTimer(Timer* timer){
timer->heapIndex=heap.size();
Heap_entry *entry=(Heap_entry*)malloc(sizeof(Heap_entry));
entry->time=timer->expires;
entry->timer=timer;
// timer->run(timer->arg);
heap.push_back(entry);
UpHeap(heap.size()-1);
// free(entry);
}
void Timer_mng::RemoveTimer(Timer* timer){
int idx=timer->heapIndex;
if (!heap.empty()&&idx<heap.size()){
if(idx==heap.size()-1){
heap.pop_back();
}
else{
SwapHeap(idx,heap.size()-1);
heap.pop_back();
int parent=(idx-1)/2; //堆中父节点编号
if(idx>0&&heap[idx]->time<heap[parent]->time)
UpHeap(idx);
else
DownHeap(idx);
}
}
}
void Timer_mng::DetectTimers(){
printf("----%lu\n",heap.size());
while(!heap.empty()){
ull now=Get_now();
if(heap[0]->time<=now){
Timer *timer=(Timer *)malloc(sizeof(Timer));
timer=heap[0]->timer;
// printf("%d\n",*(int *)heap[0]->timer->arg);
if(heap[0]->timer->run==NULL||heap[0]->timer->arg==NULL){
perror("ERROR!");
return;
}
// heap[0]->timer->run(heap[0]->timer->arg);
RemoveTimer(timer);
if(timer->ttype==CIRCLE){
timer->expires=timer->itvl+Get_now();
this->AddTimer(timer);
}
timer->OnTimer();
// printf("xxx\n");
}
}
}
void Timer_mng::UpHeap(int idx){
int parent=(idx-1)/2;
while(idx>0&&heap[idx]->time<heap[parent]->time){
SwapHeap(idx,parent);
idx=parent;
parent=(idx-1)/2;
}
}
void Timer_mng::DownHeap(int idx){
int child=idx*2+1;
while(child<heap.size()){
int minChild=(child+1==heap.size()||heap[child]->time<heap[child+1]->time)
?child:child+1;
if(heap[idx]->time<heap[minChild]->time)
break;
SwapHeap(idx,minChild);
idx=minChild;
child=idx*2+1;
}
}
void Timer_mng::SwapHeap(int idx1, int idx2){
Heap_entry *tmp=heap[idx1];
heap[idx1]=heap[idx2];
heap[idx2]=tmp;
heap[idx1]->timer->heapIndex=idx1;
heap[idx2]->timer->heapIndex=idx2;
}
ull Get_now(){
#ifdef _MSC_VER
_timeb timebuffer;
_ftime(&timebuffer);
ull ret=timebuffer.time;
ret=ret*1000+timebuffer.millitm;
return ret;
#else
timeval tv;
::gettimeofday(&tv,0);
ull ret=tv.tv_sec;
return ret*1000+tv.tv_usec/1000;
#endif
}
// init()
函数调用关系图
AddTimer()---->UpHeap()
DetectTimers()----->RemoveTimer()--->UpHeap()------|
| | | |
| | --->DownHeap()-----|
| | |
| ---------------------->SwapHeap()
|
--->OnTimer()(AddTimer())
一个简单的例子
#include<stdio.h>
#include<iostream>
#include<vector>
#include"Timer_mng.h"
using namespace std;
#define LONGTIME 8000
// 定时事件回调函数
void *onTime(void *arg){
int *id=(int *)arg;
cout<<*id<<" Game Over!"<< endl;
return nullptr;
}
int main(){
// 初始化
Timer_mng Mymng;
for(int i=0;i<10;i++){
Timer *t=(Timer *)malloc(sizeof(Timer));
int *tmp=(int *)malloc(sizeof(int));
*tmp=i;
t->Start(onTime,tmp,LONGTIME,ONCE);
//t->run(t->arg);
Mymng.AddTimer(t);
}
// while(Mymng.heap.size())
Mymng.DetectTimers();
return 0;
}
运行结果