服务器开发之定时器

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

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

1 最小堆实现

// head file //////////////////////////////////////////////////////////////////
#pragma once

#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();

private:
	friend class Timer;
    void AddTimer(Timer* timer);
    void RemoveTimer(Timer* timer);
    void DetectTimers();

    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;
	ret = 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();

private:
	friend class Timer;
	void AddTimer(Timer* timer);
	void RemoveTimer(Timer* timer);
	void DetectTimers();

	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;
	ret = ret * 1000 + tv.tv_usec / 1000;
#endif
}

结束语

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

申明

笔者自称从来不创造代码,只是代码的搬运工,例如上面最小堆是从boost中搬过来的,时间轮是从linux内核定时器中搬过来的,既然如此如果您愿意,这里代码也可以随意使用(笔者其它博客里的代码也一样),不需要笔者知晓或同意,如果您发现有什么bug或建议也请联系我。

时间: 2024-09-29 09:30:30

服务器开发之定时器的相关文章

ASP.NET MVC 中应用Windows服务以及Webservice服务开发分布式定时器

ASP.NET MVC 中应用Windows服务以及Webservice服务开发分布式定时器一:闲谈一下:1.现在任务跟踪管理系统已经开发快要结束了,抽一点时间来写一下,想一想自己就有成就感啊!!  2.关于任务跟踪管理系统项目中遇到的Windows服务以及Webservice的综合应用的问题. 大家好这是我第二次写博客 ,写的不好请大家多多谅解, 希望大家可以多多指正. 二:我稍微的整理了一下关于这个分布式定时器需求:1.根据任务跟踪管理系统中的数据库的AnswerSheet 表格中找到客户编

游戏服务器开发需要学习的技术

一,游戏服务器编程语言的选择 所谓的游戏服务器编程语言其实有很多,基本上任何一种语言都可以作为游戏服务器的编程语言.这需要根据自己游戏的类型和要求加以选择.比如C++,Java ,Erlang,go等等.目前我用过的只有C++和Java.但是以Java为主.所以接下来就以自己的经验,谈谈以Java为核心的游戏服务器开发技术体系. Java目前作为游戏服务器开发语言已经很是普遍.但是大多数是作为页游或手游的服务器,而端游戏一般选择C++,因为端游对服务器的性能要求相对比较高一些.两种语言各有利弊.

NIO原理剖析与Netty初步----浅谈高性能服务器开发(一)

除特别注明外,本站所有文章均为原创,转载请注明地址 在博主不长的工作经历中,NIO用的并不多,由于使用原生的Java NIO编程的复杂性,大多数时候我们会选择Netty,mina等开源框架,但理解NIO的原理就不重要了吗?恰恰相反,理解NIO底层机制是理解这一切的基础,由此我总结一下当初学习NIO时的笔记,以便后续复习. 以下是我理解的Java原生NIO开发大致流程: 上图大致描述的是服务端的NIO操作. 第一步,绑定一个服务的端口 这与传统阻塞IO中的ServerSocket类似,没什么好说的

手游服务器开发技术详解

从事游戏服务器开发差不多两年时间,两年间参与了不少项目,学到了很多游戏服务器开发技术,参与过几个不同架构的服务器开发,就随便聊聊游戏服务器开发需要的技术.(以下所指游戏服务器更偏向于手游,因为我对端游和页游开发接触并不多) 一.聊聊服务器开发有哪些东西要考虑. 1.开发语言的选择: 工欲善其事,必先利其器,选择一门适合的开发语法对后期开发有着事半功倍的作用. 业界主要的是c/c++ + Python/lua模式做游戏服务器.c/c++做网络通讯数据传输,python/lua做业务逻辑.这样既保持

研究python服务器开发

研究python服务器开发http://www.example-code.com/python/ssh_exec.asp telnet 118.193.152.71 51234 import socket, threading HOST = '127.0.0.1' PORT = 51234 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind((HOST, PORT)) s.listen(4) clients = [] #lis

Python服务器开发一:python基础

Python服务器开发一:python基础 Python(蟒蛇)是一种动态解释型的编程语言.Python可以在Windows.UNIX.MAC等多种操作系统上使用,也可以在Java..NET开发平台上使用. [特点] 1 Python使用C语言开发,但是Python不再有C语言中的指针等复杂的数据类型. 2 Python具有很强的面向对象特性,而且简化了面向对象的实现.它消除了保护类型.抽象类.接口等面向对象的元素. 3 Python代码块使用空格或制表符缩进的方式分隔代码. 4 Python仅

Python服务器开发二:Python网络基础

Python服务器开发二:Python网络基础 网络由下往上分为物理层.数据链路层.网络层.传输层.会话层.表示层和应用层. HTTP是高层协议,而TCP/IP是个协议集,包过许多的子协议.包括:传输层的 FTP,UDP,TCP协议等,网络层的ip协议等,高层协议如HTTP,telnet协议等,HTTP是TCP/IP的一个子协议. socket是对TCP/IP协议的封装和应用(程序员层面上).也可以说,TPC/IP协议是传输层协议,主要解决数据如何在网络中传输,而HTTP是应用层协议,主要解决如

Python服务器开发三:Socket

Python服务器开发三:Socket socket是操作系统中I/O的延续,它可以使进程和机器之间的通信成为可能.socket可以看成一个标准的文件描述符.不同的是文件需要用open()函数打开,而socket用socket() 函数建立.recv().send()函数和read().write()函数极为相似. TCP一般通过accept()来为每个连接的客户端建立一个新的scoket.UDP一般只是使用一个单一的socket,完全依靠recvfrom()返回的值来判断该往哪里发送响应. 客

linux服务器开发浅谈

[开发前准备] 在进行linux服务器开发之前,必须很清楚地了解所开发的对象需要考虑的相关问题比如:功能架构:提供服务的模块体系结构稳定性:服务器的出core率,内存泄露情况性能:请求与返回的速度与正确性负载能力:能同时访问的最大数量和频度 根据不同服务器对象的环境和应用,服务器开发的对应手段相差甚远.比如就客户端连接时间较短却又比较频繁的服务器(例如HTTP服务器)而言,在可选的服务器结构中,预先派生进/线程的结构就要比并发式结构高效 总之,在开发服务器之前,必须进行完整的服务器开发需求分析,