KBEngine奇技淫巧<四>

19 篇文章 0 订阅
16 篇文章 0 订阅

定时器介绍

是时候放出灵魂框架图了,KBEngine最重要的几个类
KBENgine引擎最重要的几个类
是的,这个图就说明了KBEngine引擎最重要的几个类。从程序来看,最重要的就是App,因为我是从BaseApp为例子说明的,就画了BaseApp,其他App大同小异,原理类似。

其实最核心的类还是EventDispatcher。

EventDispatcher

EventDispatcher是引擎的核心的核心,可以说引擎能跑起来几乎是因为EventDispatcher在起作用。

void EventDispatcher::processUntilBreak()
{
	if(breakProcessing_ != EVENT_DISPATCHER_STATUS_BREAK_PROCESSING)
		breakProcessing_ = EVENT_DISPATCHER_STATUS_RUNNING;

	while(breakProcessing_ != EVENT_DISPATCHER_STATUS_BREAK_PROCESSING)
	{
		this->processOnce(true);
	}
}

这While循环就是引擎的主循环,可以看到循环是一帧一帧的跑起来的,我们再看每一帧都在干什么。

int EventDispatcher::processOnce(bool shouldIdle)
{
	if(breakProcessing_ != EVENT_DISPATCHER_STATUS_BREAK_PROCESSING)
		breakProcessing_ = EVENT_DISPATCHER_STATUS_RUNNING;

	this->processTasks();

	if(breakProcessing_ != EVENT_DISPATCHER_STATUS_BREAK_PROCESSING){
		this->processTimers();
	}

	this->processStats();
	
	if(breakProcessing_ != EVENT_DISPATCHER_STATUS_BREAK_PROCESSING){
		return this->processNetwork(shouldIdle);
	}

	return 0;
}

最重要的一个函数,处理任务,处理定时器,处理network,中间有一个处理状态的,是分析辅助用的,可以说是不是核心功能,我们在奇技淫巧三里面讨论过Task,这篇文章说说Timer定时器。

Timer 时间 定时器

可以说和时间相关的处理逻辑在这地方,和Task,操作处理设计理念类似。在EventDispatcher里面有一个管理器管理着所有的定时器,游戏循环里面检查定时器触发条件,如果达到就处理相关的定时事件。
我们看下标哥是如何组织代码的:
看的时候要看清楚是Time还是Timer,概念上来说Timer是比Time高一个层次的,Timer涉及到的就是处理逻辑一类,Time就是时间,涉及到的就是如何达到触发条件。代码有继承和内嵌类,稍微有点复杂。
在这里插入图片描述试着可以说说,TimersT是管理所有定时器的管理类,它里面有两个比较关键的成员变量:

container_

	typedef std::vector<KBEngine::TimeBase *> Container;
	Container container_;

这个变量是一个Time的vector,主要是不需要排序类的定时器,没有优先级。TimeBase就是主要的定时器设置。

timeQueue_

	PriorityQueue	timeQueue_;

可以看出来,这个也是容器,是一个优先队列的容器,里面存放的是一些需要关注优先级,也就是谁先触发之类逻辑的定时器。为了看的明显点,贴上代码:

TimerT

class TimersBase
{
public:
	virtual void onCancel() = 0;
};

template<class TIME_STAMP>
class TimersT : public TimersBase
{
public:
	typedef TIME_STAMP TimeStamp;

	TimersT();
	virtual ~TimersT();
	
	inline uint32 size() const	{ return timeQueue_.size(); }
	inline bool empty() const	{ return timeQueue_.empty(); }
	
	int	process(TimeStamp now);
	bool legal( TimerHandle handle ) const;
	TIME_STAMP nextExp( TimeStamp now ) const;
	void clear( bool shouldCallCancel = true );
	
	bool getTimerInfo( TimerHandle handle, 
					TimeStamp& time, 
					TimeStamp&	interval,
					void *&	pUser ) const;
	
	TimerHandle	add(TimeStamp startTime, TimeStamp interval,
						TimerHandler* pHandler, void * pUser);
	
private:
	
	typedef std::vector<KBEngine::TimeBase *> Container;
	Container container_;

	void purgeCancelledTimes();
	void onCancel();

	class Time : public TimeBase
	{
	public:
		Time( TimersBase & owner, TimeStamp startTime, TimeStamp interval,
			TimerHandler * pHandler, void * pUser );

		TIME_STAMP time() const			{ return time_; }
		TIME_STAMP interval() const		{ return interval_; }

		void triggerTimer();

	private:
		TimeStamp			time_;
		TimeStamp			interval_;

		Time( const Time & );
		Time & operator=( const Time & );
	};

	class Comparator
	{
	public:
		bool operator()(const Time* a, const Time* b)
		{
			return a->time() > b->time();
		}
	};
	
	class PriorityQueue
	{
	public:
		typedef std::vector<Time *> Container;

		typedef typename Container::value_type value_type;
		typedef typename Container::size_type size_type;

		bool empty() const				{ return container_.empty(); }
		size_type size() const			{ return container_.size(); }

		const value_type & top() const	{ return container_.front(); }

		void push( const value_type & x )
		{
			container_.push_back( x );
			std::push_heap( container_.begin(), container_.end(),
					Comparator() );
		}

		void pop()
		{
			std::pop_heap( container_.begin(), container_.end(), Comparator() );
			container_.pop_back();
		}

		Time * unsafePopBack()
		{
			Time * pTime = container_.back();
			container_.pop_back();
			return pTime;
		}

		Container & container()		{ return container_; }

		void make_heap()
		{
			std::make_heap( container_.begin(), container_.end(),
					Comparator() );
		}

	private:
		Container container_;
	};
	
	PriorityQueue	timeQueue_;
	Time * 			pProcessingNode_;
	TimeStamp 		lastProcessTime_;
	int				numCancelled_;

	TimersT( const TimersT & );
	TimersT & operator=( const TimersT & );

};

typedef TimersT<uint32> Timers;
typedef TimersT<uint64> Timers64;
}

最后的Timers64 是EventDispatcher里面变量类型,TimersT就是主要管理着两个定时器容器。
我们可以看Time的实现:

Time

class TimersBase
{
public:
	virtual void onCancel() = 0;
};

class Time : public TimeBase
	{
	public:
		Time( TimersBase & owner, TimeStamp startTime, TimeStamp interval,
			TimerHandler * pHandler, void * pUser );

		TIME_STAMP time() const			{ return time_; }
		TIME_STAMP interval() const		{ return interval_; }

		void triggerTimer();

	private:
		TimeStamp			time_;
		TimeStamp			interval_;

		Time( const Time & );
		Time & operator=( const Time & );
	};

虽然Time是Timer里面声明的内嵌类,但是他的父类是在TimerT外边声明的,为了方便我放到了一起。
在Time里面放着TimerHandler,这个就是引擎使用的具体的业务类了。比如:

class NetworkInterface : public TimerHandler
{
public:
	typedef std::map<Address, Channel *>	ChannelMap;

不管在哪儿实现的TimerHandler,想办法把它偷偷加入到我们刚说的两个容器里面,就可以执行了。梳理一下
在这里插入图片描述可以这样说
App里面有一个EventDispatcher,EventDispatcher里面有一个Timer管理器,管理器有一个Time容器【队列,container_变量没有用,貌似标哥没有想好】,Time里面存放着定时器处理器,定时器处理提TimerHandler就是需要自己实现的业务逻辑。

读这块代码的时候需要看清Timer和Time的区别,TimerHandle和TimerHandler的区别。

Timer和Time的区别就是Timer是管理器,Time是存放TimerHandler的容器。
TimerHandle和TimerHandler区别是TimeHandle是一个暂时未知的设计,TimerHandler
是一个具体的业务定时器处理逻辑。
我们可以看一下定时器处理遍历:

process

template <class TIME_STAMP>
int TimersT< TIME_STAMP >::process(TimeStamp now)
{
	int numFired = 0;

	while ((!timeQueue_.empty()) && (
		timeQueue_.top()->time() <= now ||
		timeQueue_.top()->isCancelled()))
	{
		Time * pTime = pProcessingNode_ = timeQueue_.top();
		timeQueue_.pop();

		if (!pTime->isCancelled())
		{
			++numFired;
			pTime->triggerTimer();
		}

		if (!pTime->isCancelled())
		{
			timeQueue_.push( pTime );
		}
		else
		{
			delete pTime;

			KBE_ASSERT( numCancelled_ > 0 );
			--numCancelled_;
		}
	}

	pProcessingNode_ = NULL;
	lastProcessTime_ = now;
	return numFired;
}

这个循环也是一个很有意思的循环,他是把队列里面的所有元素都操作一遍的循环。
从头开始,拿出头来,看触发没,触发则触发,没触发就增加时间。
循环结束条件是队列头时间不小于当前时间。就是遍历了一遍。写的也是很精巧,不过就是有一个问题,如果定时事件过多,比如上千个,因为数量级到了1000在一个循环里面会造成性能下降,有可能卡帧,服务器还好些吧,而且也很难达到这量级。暂时是一个可以优化的地方。

再看一下添加逻辑

add

template <class TIME_STAMP>
TimerHandle TimersT< TIME_STAMP >::add( TimeStamp startTime,
		TimeStamp interval, TimerHandler * pHandler, void * pUser )
{
	Time * pTime = new Time( *this, startTime, interval, pHandler, pUser );
	timeQueue_.push( pTime );
	return TimerHandle( pTime );
}

添加逻辑也是很诡异,返回一个TimerHandle,我不是很理解,难道这个东西是句柄的意思吗,将来准备某种地方使用?我看使用的地方很少有接住这个返回的变量的,是一个疑问点。

总结一下

EventDispatcher最重要的三个成员变量我们就说了两个了,Task和Timer,还剩下的一个是EventPoller管网络的。
Task和Timer设计理念类似,都是找时机添加对应的逻辑,坐等游戏循环调用。在以后调试或者阅读这方面的代码的时候,寻找相关的逻辑准没错。
真是读的越多理解的越多!

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

当当小螳螂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值