高性能服务器开发之C++定时器
高性能服务器开发之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 //////////////////////////////////////////////////////////////////////////
// Timer Timer::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_();
} //////////////////////////////////////////////////////////////////////////
// TimerManager void 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);
else
DownHeap(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;
#else
timeval 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) //////////////////////////////////////////////////////////////////////////
// Timer Timer::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_();
} //////////////////////////////////////////////////////////////////////////
// TimerManager TimerManager::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;
#else
timeval tv;
::gettimeofday(&tv, 0);
unsigned long long ret = tv.tv_sec;
return ret * 1000 + tv.tv_usec / 1000;
#endif
}
结束语
在曾经的很多项目中,定时器的实现都是使用map,也许效率不是太高,却从来没有成为性能的瓶颈。但是程序员通常是追求完美的,既然有更好解决方案,且其实现又不那么复杂,那就完全可以去尝试。
高性能服务器开发之C++定时器的更多相关文章
- 高性能WEB开发之Web性能测试工具推荐
Firebug: Firebug 是firefox中最为经典的开发工具,可以监控请求头,响应头,显示资源加载瀑布图: HttpWatch: httpwatch 功能类似firebug,可以监控请求头, ...
- JSON的服务器开发之路
JSON的服务器开发之路 不知道需要哪儿些包... /dcywpt/WebRoot/WEB-INF/lib/commons-collections-3.2.jar /dcywpt/WebRoot/WE ...
- 服务器开发之CGI后门
1.html代码 <form id = "form" name="form" method="post" action=". ...
- JavaEE开发之SpringMVC中的静态资源映射及服务器推送技术
在上篇博客中,我们聊了<JavaEE开发之SpringMVC中的自定义拦截器及异常处理>.本篇博客我们继续的来聊SpringMVC的东西,下方我们将会聊到js.css这些静态文件的加载配置 ...
- JavaEE开发之Spring中的多线程编程以及任务定时器详解
上篇博客我们详细的聊了Spring中的事件的发送和监听,也就是常说的广播或者通知一类的东西,详情请移步于<JavaEE开发之Spring中的事件发送与监听以及使用@Profile进行环境切换&g ...
- Cocos2d-x 3.x游戏开发之旅
Cocos2d-x 3.x游戏开发之旅 钟迪龙 著 ISBN 978-7-121-24276-2 2014年10月出版 定价:79.00元 516页 16开 内容提要 <Cocos2d-x ...
- iOS多线程开发之GCD(中篇)
前文回顾: 上篇博客讲到GCD的实现是由队列和任务两部分组成,其中获取队列的方式有两种,第一种是通过GCD的API的dispatch_queue_create函数生成Dispatch Queue:第二 ...
- .NET Core 跨平台 GUI 开发之 GTtkSharp 初级篇
.NET Core 跨平台 GUI 开发之 GTtkSharp 初级篇 本文作为初级篇,适合已经安装好.NET Core 环境以及 Gtk 环境,并具备了 C#开发基础知识,能跑一些简单的例子,希望更 ...
- iOS多线程开发之GCD(中级篇)
前文回顾: 上篇博客讲到GCD的实现是由队列和任务两部分组成,其中获取队列的方式有两种,第一种是通过GCD的API的dispatch_queue_create函数生成Dispatch Queue:第二 ...
随机推荐
- PHP导出数据到表格的实例
我发现最近的项目需要导出Excel表的页面非常的多,想来这个也是我们常用的功能了,现在我可以很熟练的导出了,但是记得当时自己第一次导出时还是绕了一些弯路的,那么现在我就来记录下我这次用exshop框架 ...
- ethereum(以太坊)(基础)--容易忽略的坑(一)
pragma solidity ^0.4.0; contract base{ address public _owner=msg.sender; uint _a; string internal _b ...
- Java : java基础(4) 线程
java开启多线程的方式,第一种是新建一个Thread的子类,然后重写它的run()方法就可以,调用类的对象的start()方法,jvm就会新开一个线程执行run()方法. 第二种是类实现Runabl ...
- symfony 数据库使用(二)
symfony可以根据数据用已经有表反向生成实体,以3.3.*为例: php bin/console doctrine:mapping:import --force AppBundle xml 从现有 ...
- Hadoop(11)-MapReduce概述和简单实操
1.MapReduce的定义 2.MapReduce的优缺点 优点 缺点 3.MapReduce的核心思想 4.MapReduce进程 5.常用数据序列化类型 6.MapReduce的编程规范 用户编 ...
- PrestaShop 网站漏洞修复如何修复
PrestaShop网站的漏洞越来越多,该网站系统是很多外贸网站在使用的一个开源系统,从之前的1.0初始版本到现在的1.7版本,经历了多次的升级,系统使用的人也越来越多,国内使用该系统的外贸公司也很多 ...
- Horner规则求多项式
/* Horner */ /*多项式:A(x)=a[n]X^n+a[n-1]x^n-1+...+a[1]X^1+a[0]X^0*/ #include <stdio.h> long int ...
- (数据科学学习手札22)主成分分析法在Python与R中的基本功能实现
上一篇中我们详细介绍推导了主成分分析法的原理,并基于Python通过自编函数实现了挑选主成分的过程,而在Python与R中都有比较成熟的主成分分析函数,本篇我们就对这些方法进行介绍: R 在R的基础函 ...
- SELECT(データ取得)
WHERE 句は.満たすべき条件を指定することにより選択される行数を制限します. WHERE 句は.SELECT 命令と同様に OPEN CURSOR.UPDATE.および DELETE 命令でも使用 ...
- 用ServiceStack操作使用redis的问题
最近在学习Redis,查阅网上很多资料后使用SericeStack连接redis.在nuget中下载ServiceStack.Redis,主要使用到四个dll 但是运行之后会出现一堆奇怪问题:没有实现 ...