muduo网络库源码复现笔记(十九):TimeQueue定时器

Muduo网络库简介

muduo 是一个基于 Reactor 模式的现代 C++ 网络库,作者陈硕。它采用非阻塞 IO 模型,基于事件驱动和回调,原生支持多核多线程,适合编写 Linux 服务端多线程网络应用程序。
muduo网络库的核心代码只有数千行,在网络编程技术学习的进阶阶段,muduo是一个非常值得学习的开源库。目前我也是刚刚开始学习这个网络库的源码,希望将这个学习过程记录下来。这个网络库的源码已经发布在GitHub上,可以点击这里阅读。目前Github上这份源码已经被作者用c++11重写,我学习的版本是没有使用c++11版本的。不过二者大同小异,核心思想是没有变化的。点这里可以看我的源代码。从笔记十七开始记录muduo的net库的实现过程。如果你需要看一下基础库(base)的复现过程,可以点击这里:muduo的base库实现过程。而网络库的笔记在这里:
muduo网络库源码复现笔记(十七):什么都不做的EventLoop
muduo网络库源码复现笔记(十八):Reactor的关键结构

TimeQueue定时器

前面我们实现了一个初步的Reactor结构,这一节将加上定时器功能。举例来说,下面的代码实现了下面这样一个功能:定时10s之后执行task函数,和每隔2秒后执行task2函数。

EventLoop loop;
loop.runAfter(10,boost::bind(task,"once"));
loop.runAfter(2,boost::bind(task2,"every 2"));
loop.loop()

为了实现这样一个功能,需要TimerId,Timer,TimerQueue三个类。其中只有TimerId是对用户可见的。下面讲解一下这三个类。

1 TimeId类

TimeId不难,它只有两个属性,一个是它对应的定时器,一个是定时器的序号。

class TimerId : public muduo::copyable
{
public:
	TimerId()
		:	timer_(NULL),
			sequence_(0)
	{
	}
	
	TimerId(Timer* timer,int64_t seq)
		:	timer_(timer),
			sequence_(seq)
	{
	}

	friend class TimerQueue;
private:
	Timer* timer_;
	int64_t sequence_;
};

2 Timer类

Timer类是定时器类,它的相关属性均在注释中。我们将使用run函数来执行callback任务。在TimerQueue中我们将每个定时器绑定到定时器文件描述符timerfd上,到时的定时器会被拿出执行任务。

Timer(const TimerCallback& cb,Timestamp when,double interval)
		:	callback_(cb),
			expiration_(when),
			interval_(interval),
			repeat_(interval > 0),
			sequence_(s_numCreated_.incrementAndGet())
	{
	}

	void run() const
	{
		callback_();
	}

	Timestamp expiration() const
	{
		return expiration_;
	}

	bool repeat() const {return repeat_;}
	
	int64_t sequence() const {return sequence_;}

	void restart(Timestamp now);
	
	static int64_t numCreated() {return s_numCreated_.get();}
			
private:
	const TimerCallback callback_;  //要执行的任务
	Timestamp expiration_; //执行时间
	const double interval_; //重复间隔
	const bool repeat_;  //是否需要重复
	const int64_t sequence_; //序号
	
	static AtomicInt64 s_numCreated_; //定时器数目统计
};

3 TimerQueue类

TimerQueue是实现定时功能的重点,它可以加入与去除定时器、取出到时的定时器。注意TimerQueue两个成员timerfd_和timerfdChannel_。前者是一个时间文件描述符,后者是一个Channel,它的作用是监听定时器的到时时刻,在TimerQueue初始化时加入Poller的Channels_。定时器所在的容器是TimerList和ActiveTimerSet,二者大致相似的。都是一个set,但是前者的元素类型是pair,它的排序方式是按到时时间先后;后者是pair,排序方式是按定时器的内存地址。

class TimerQueue : boost::noncopyable
{
public:
	TimerQueue(EventLoop* loop);
	~TimerQueue();
	
	TimerId addTimer(const TimerCallback& cb,
				     Timestamp when,
					 double interval);
	
	void cancel(TimerId timerId);
private:
	typedef std::pair<Timestamp,Timer*> Entry;
	typedef std::set<Entry> TimerList;
	typedef std::pair<Timer*,int64_t> ActiveTimer;
	typedef std::set<ActiveTimer> ActiveTimerSet;

	void addTimerInLoop(Timer* timer);
	void cancelInLoop(TimerId timerId);
	//called when timerfd alarms
	void handleRead();
	std::vector<Entry> getExpired(Timestamp now);
	void reset(const std::vector<Entry>& expired,Timestamp now);
	
	bool insert(Timer* timer);

	EventLoop* loop_;
	const int timerfd_;
	Channel timerfdChannel_;
	TimerList timers_; //timers sorted by time

	ActiveTimerSet activeTimers_;
	bool callingExpiredTimers_;
	ActiveTimerSet cancelingTimers_;
};

3.1 getExpired

getExpired用于取出到时的定时器,并加入TimerList和ActiveTimerSet。这个函数需要关注的地方是sentry这个哨兵。它的pair对的first是now,second是转换为Timer指针的最大内存地址。这样确保我们在使用low_bound时得到的end的first总是大于now。

std::vector<TimerQueue::Entry> TimerQueue::getExpired(Timestamp now)
{
	assert(timers_.size() == activeTimers_.size());
	std::vector<Entry> expired;
	Entry sentry(now,reinterpret_cast<Timer*>(UINTPTR_MAX));
	TimerList::iterator end = timers_.lower_bound(sentry);
	assert(end == timers_.end() || now < end->first);
	std::copy(timers_.begin(),end,back_inserter(expired));
	timers_.erase(timers_.begin(),end);

	for(std::vector<TimerQueue::Entry>::iterator it = expired.begin(); it != expired.end(); ++it)
	{
		ActiveTimer timer(it->second,it->second->sequence());
		size_t n = activeTimers_.erase(timer);
		assert(n == 1); (void)n;
	}
	assert(timers_.size() == activeTimers_.size());
	return expired;
}

时序图

时序图如下,当我们调用loop函数时,得到activeChannels,然后处理各Channel,对于定时器Channel,拿到到时的定时器然后执行它所携带的任务函数。
muduo网络库源码复现笔记(十九):TimeQueue定时器_第1张图片

你可能感兴趣的:(muduo)