高性能服务器开发之C++定时器

来源: https://www.cnblogs.com/junye/p/5836552.html

写这篇文章前搜了下网上类似的文章,有很多,所以笔者的这篇文章就不对定时器的常见实现方法加以说明,也不进行性能比较,直接上代码。

基于multimap实现的比较简单,这里略过。

前导

对于大多数的服务器程序,其定时器一般支持单线程就够了,一般使用方法见下面代码。如果需要多线程怎么办,笔者一般用一个简单的办法:多线程的业务线程中不包含定时器管理器,单独启一个线程用来管理所有定时器,当时间触发时,向业务线程投递定时器消息即可。

#include <iostream>
#include <thread>
#include <chrono>
#include "Timer.h"void TimerHandler()
{std::cout << "TimerHandler" << std::endl;
}int main()
{TimerManager tm;Timer t(tm);t.Start(&TimerHandler, 1000);while (true){tm.DetectTimers();std::this_thread::sleep_for(std::chrono::milliseconds(100));}std::cin.get();return 0;
}

1 最小堆实现

#include <vector>
#include <boost/function.hpp>class TimerManager;class Timer
{
public:enum TimerType { ONCE, CIRCLE };Timer(TimerManager& manager);~Timer();template<typename Fun>void Start(Fun fun, unsigned interval, TimerType timeType = CIRCLE);void Stop();private:void OnTimer(unsigned long long now);private:friend class TimerManager;TimerManager& manager_;TimerType timerType_;boost::function<void(void)> timerFun_;unsigned interval_;unsigned long long expires_;size_t heapIndex_;
};class TimerManager
{
public:static unsigned long long GetCurrentMillisecs();void DetectTimers();
private:friend class Timer;void AddTimer(Timer* timer);void RemoveTimer(Timer* timer);void UpHeap(size_t index);void DownHeap(size_t index);void SwapHeap(size_t, size_t index2);private:struct HeapEntry{unsigned long long time;Timer* timer;};std::vector<HeapEntry> heap_;
};template<typename Fun>
inline void Timer::Start(Fun fun, unsigned interval, TimerType timeType)
{Stop();interval_ = interval;timerFun_ = fun;timerType_ = timeType;timer->expires_ = timer->interval_ + TimerManager::GetCurrentMillisecs();manager_.AddTimer(this);
}// cpp file //
#define _CRT_SECURE_NO_WARNINGS
#include "config.h"
#include "timer.h"
#ifdef _MSC_VER
# include <sys/timeb.h>
#else
# include <sys/time.h>
#endif//
// TimerTimer::Timer(TimerManager& manager): manager_(manager), heapIndex_(-1)
{
}Timer::~Timer()
{Stop();
}void Timer::Stop()
{if (heapIndex_ != -1){manager_.RemoveTimer(this);heapIndex_ = -1;}
}void Timer::OnTimer(unsigned long long now)
{if (timerType_ == Timer::CIRCLE){expires_ = interval_ + now;manager_.AddTimer(this);}else{heapIndex_ = -1;}timerFun_();
}//
// TimerManagervoid TimerManager::AddTimer(Timer* timer)
{timer->heapIndex_ = heap_.size();HeapEntry entry = { timer->expires_, timer };heap_.push_back(entry);UpHeap(heap_.size() - 1);
}void TimerManager::RemoveTimer(Timer* timer)
{size_t index = timer->heapIndex_;if (!heap_.empty() && index < heap_.size()){if (index == heap_.size() - 1){heap_.pop_back();}else{SwapHeap(index, heap_.size() - 1);heap_.pop_back();size_t parent = (index - 1) / 2;if (index > 0 && heap_[index].time < heap_[parent].time)UpHeap(index);elseDownHeap(index);}}
}void TimerManager::DetectTimers()
{unsigned long long now = GetCurrentMillisecs();while (!heap_.empty() && heap_[0].time <= now){Timer* timer = heap_[0].timer;RemoveTimer(timer);timer->OnTimer(now);}
}void TimerManager::UpHeap(size_t index)
{size_t parent = (index - 1) / 2;while (index > 0 && heap_[index].time < heap_[parent].time){SwapHeap(index, parent);index = parent;parent = (index - 1) / 2;}
}void TimerManager::DownHeap(size_t index)
{size_t child = index * 2 + 1;while (child < heap_.size()){size_t minChild = (child + 1 == heap_.size() || heap_[child].time < heap_[child + 1].time)? child : child + 1;if (heap_[index].time < heap_[minChild].time)break;SwapHeap(index, minChild);index = minChild;child = index * 2 + 1;}
}void TimerManager::SwapHeap(size_t index1, size_t index2)
{HeapEntry tmp = heap_[index1];heap_[index1] = heap_[index2];heap_[index2] = tmp;heap_[index1].timer->heapIndex_ = index1;heap_[index2].timer->heapIndex_ = index2;
}unsigned long long TimerManager::GetCurrentMillisecs()
{
#ifdef _MSC_VER_timeb timebuffer;_ftime(&timebuffer);unsigned long long ret = timebuffer.time;ret = ret * 1000 + timebuffer.millitm;return ret;
#elsetimeval tv;         ::gettimeofday(&tv, 0);unsigned long long ret = tv.tv_sec;return ret * 1000 + tv.tv_usec / 1000;
#endif
}

2 时间轮实现

// header file //
#pragma once
#include <list>
#include <vector>
#include <boost/function.hpp>class TimerManager;class Timer
{
public:enum TimerType {ONCE, CIRCLE};Timer(TimerManager& manager);~Timer();template<typename Fun>void Start(Fun fun, unsigned interval, TimerType timeType = CIRCLE);void Stop();private:void OnTimer(unsigned long long now);private:friend class TimerManager;TimerManager& manager_;TimerType timerType_;boost::function<void(void)> timerFun_;unsigned interval_;unsigned long long expires_;int vecIndex_;std::list<Timer*>::iterator itr_;
};class TimerManager
{
public:TimerManager();static unsigned long long GetCurrentMillisecs();void DetectTimers();private:friend class Timer;void AddTimer(Timer* timer);void RemoveTimer(Timer* timer);int Cascade(int offset, int index);private:typedef std::list<Timer*> TimeList;std::vector<TimeList> tvec_;unsigned long long checkTime_;
};template<typename Fun>
inline void Timer::Start(Fun fun, unsigned interval, TimerType timeType)
{Stop();interval_ = interval;timerFun_ = fun;timerType_ = timeType;expires_ = interval_ + TimerManager::GetCurrentMillisecs();manager_.AddTimer(this);
}// cpp file //#define _CRT_SECURE_NO_WARNINGS
#include "config.h"
#include "timer2.h"
#ifdef _MSC_VER
# include <sys/timeb.h>
#else
# include <sys/time.h>
#endif#define TVN_BITS 6
#define TVR_BITS 8
#define TVN_SIZE (1 << TVN_BITS)
#define TVR_SIZE (1 << TVR_BITS)
#define TVN_MASK (TVN_SIZE - 1)
#define TVR_MASK (TVR_SIZE - 1)
#define OFFSET(N) (TVR_SIZE + (N) *TVN_SIZE)
#define INDEX(V, N) ((V >> (TVR_BITS + (N) *TVN_BITS)) & TVN_MASK)//
// TimerTimer::Timer(TimerManager& manager): manager_(manager), vecIndex_(-1)
{
}Timer::~Timer()
{Stop();
}void Timer::Stop()
{if (vecIndex_ != -1){manager_.RemoveTimer(this);vecIndex_ = -1;}
}void Timer::OnTimer(unsigned long long now)
{if (timerType_ == Timer::CIRCLE){expires_ = interval_ + now;manager_.AddTimer(this);}else{vecIndex_ = -1;}timerFun_();
}//
// TimerManagerTimerManager::TimerManager()
{tvec_.resize(TVR_SIZE + 4 * TVN_SIZE);checkTime_ = GetCurrentMillisecs();
}void TimerManager::AddTimer(Timer* timer)
{unsigned long long expires = timer->expires_;unsigned long long idx = expires - checkTime_;if (idx < TVR_SIZE){timer->vecIndex_ = expires & TVR_MASK;}else if (idx < 1 << (TVR_BITS + TVN_BITS)){timer->vecIndex_ = OFFSET(0) + INDEX(expires, 0);}else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS)){timer->vecIndex_ = OFFSET(1) + INDEX(expires, 1);}else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)){timer->vecIndex_ = OFFSET(2) + INDEX(expires, 2);}else if ((long long) idx < 0){timer->vecIndex_ = checkTime_ & TVR_MASK;}else{   if (idx > 0xffffffffUL){idx = 0xffffffffUL;expires = idx + checkTime_;}timer->vecIndex_ = OFFSET(3) + INDEX(expires, 3);}TimeList& tlist = tvec_[timer->vecIndex_];tlist.push_back(timer);timer->itr_ = tlist.end();--timer->itr_;
}void TimerManager::RemoveTimer(Timer* timer)
{TimeList& tlist = tvec_[timer->vecIndex_];tlist.erase(timer->itr_);
}void TimerManager::DetectTimers()
{unsigned long long now = GetCurrentMillisecs();while (checkTime_ <= now){int index = checkTime_ & TVR_MASK;if (!index &&!Cascade(OFFSET(0), INDEX(checkTime_, 0)) &&!Cascade(OFFSET(1), INDEX(checkTime_, 1)) &&!Cascade(OFFSET(2), INDEX(checkTime_, 2))){Cascade(OFFSET(3), INDEX(checkTime_, 3));}++checkTime_;TimeList& tlist = tvec_[index];TimeList temp;temp.splice(temp.end(), tlist);for (TimeList::iterator itr = temp.begin(); itr != temp.end(); ++itr){(*itr)->OnTimer(now);}}
}int TimerManager::Cascade(int offset, int index)
{TimeList& tlist = tvec_[offset + index];TimeList temp;temp.splice(temp.end(), tlist);for (TimeList::iterator itr = temp.begin(); itr != temp.end(); ++itr){AddTimer(*itr);}return index;
}unsigned long long TimerManager::GetCurrentMillisecs()
{
#ifdef _MSC_VER_timeb timebuffer;_ftime(&timebuffer);unsigned long long ret = timebuffer.time;ret = ret * 1000 + timebuffer.millitm;return ret;
#elsetimeval tv;         ::gettimeofday(&tv, 0);unsigned long long ret = tv.tv_sec;return ret * 1000 + tv.tv_usec / 1000;
#endif
}

结束语

在曾经的很多项目中,定时器的实现都是使用map,也许效率不是太高,却从来没有成为性能的瓶颈。但是程序员通常是追求完美的,既然有更好解决方案,且其实现又不那么复杂,那就完全可以去尝试。

转载于:https://www.cnblogs.com/lsgxeva/p/8072468.html

高性能服务器开发之C++定时器相关推荐

  1. Java游戏服务器开发之A星算法

    Java游戏服务器开发之A星算法    学习这个主要是用于寻路算法.    参考资料主要是siki学院的视频,A计划--人工智能--A星算法. 网址http://www.sikiedu.com/cou ...

  2. 高性能WEB开发之Web性能测试工具推荐

    Firebug: Firebug 是firefox中最为经典的开发工具,可以监控请求头,响应头,显示资源加载瀑布图: HttpWatch : httpwatch 功能类似firebug,可以监控请求头 ...

  3. Linux服务器开发之:chmod()函数,chmod命令,以及文件屏蔽umask命令,程序修改umask,详细介绍+案例演示

     1.依赖的头文件 #include<sys/stat.h> 2.函数定义: //通过传入path中给定的文件名的方式来改变文件制定的权限 int chmod(const char * ...

  4. Linux服务器开发之:stat(),fstat(),lstat()详细介绍+案例演示

     1.依赖的头文件 #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> 2.函数定 ...

  5. 黑马服务器开发之linux基础编程视频——笔记

    目录 视频链接:13_软硬链接_哔哩哔哩_bilibili 注:命令操作中的文件名,若是当前文件夹下的文件,可不加路径,若是其他文件夹下的,要添加路径. 1. 命令解析器:shell 2. Linux ...

  6. ios即时通讯客户端开发之-mac上搭建openfire服务器

    CHENYILONG Blog ios即时通讯客户端开发之-mac上搭建openfire服务器 转自:月光的尽头 ios即时通讯客户端开发之-mac上搭建openfire服务器 一.下载并安装open ...

  7. Linux 高性能服务器开发笔记:Reactor 模型定时器 | 网络编程定时器

    本文主要根据游双书本 Linux 高性能服务器开发 学习分析 linux 网络编程常用到的定时器模型,配备详细理解和分析,同时分析了 Linux 内核中定时器的低精度时间轮和高精度定时器实现思路还有 ...

  8. bpmn 文件 服务器部署,Liferay7 BPM门户开发之45: 集成Activiti文件上传部署流程BPMN模型...

    开发文件上传,部署流程模板. 首先,开发jsp页面,deploy.jsp ${RETURN_MESSAGE} 其中,上传form的action为portlet:actionURL,它的name就是在p ...

  9. unity深入研究--开发之C#使用Socket与HTTP连接服务器传输数据包

    unity深入研究--开发之C#使用Socket与HTTP连接服务器传输数据包 转载 2013年01月04日 09:01:15 2243 最近比较忙,有段时间没写博客拉.最近项目中需要使用HTTP与S ...

最新文章

  1. 定时分量和直流分量_直流电机效率测试的计算与纹波因数及波形因数的计算
  2. SAP 谈谈PFCG创建ROLE后打包产生TR
  3. Docker技术实践——中级篇
  4. matlab从矩阵中取rp开头文件,matlab trainrp
  5. 融合时代 存储迎来无限可能
  6. JSF请求处理过程(一) FacesServlet初始化
  7. Linux——cmake使用示例与整理总结
  8. VMware虚拟机文件
  9. web前端(2)——了解什么是前端,以及与后端的关系
  10. 关于信息熵的简单理解
  11. java用信号量写理发师_课内资源 - 基于Java实现的生产者与消费者问题、读者写者问题、哲学家进餐问题、理发师睡觉问题、医生看病问题...
  12. 国际版(英文版)Skype使用国内卡打电话(转)
  13. scMRA:一种健壮的深度学习方法,可以用多个参考数据集注释scRNA-seq数据
  14. 用HTML 格式导出Excel 时,如何保留显示网格线 转载
  15. MATLAB之黄金分割法求极值
  16. jq使用请求报405错误
  17. 这4个资源搜索网站,你可能还不知道,但非常的实用
  18. 防止首网页篡改 html,网站web前端的加密方式 防止数据被篡改
  19. 面试华为必备:华为18级大牛呕心沥血三年整理的 趣谈网络协议
  20. android常用刷机指令,刷机以及常用命令

热门文章

  1. 加载场景不销毁的实现
  2. redis部署与卸载
  3. Grafana分析Nginx日志
  4. 分页原理+软件架构师
  5. first-软件工程
  6. object-c 随机数总结
  7. 局域网PING的TIME值都超高的一种解决方案
  8. html visibility 替代服务器端控件 visable属性!
  9. 很是迷茫 ERP和HIS哪个更有发展前途?
  10. canvas绘制三角形