Muduo网络库的实现TimerQueue定时器(五)

源码下载以及安装点击链接https://blog.csdn.net/YoungSusie/article/details/90021742

分类 Muduo网络库编程 学习笔记

给前面的EventLoop 加上定时的功能,传统的Reactor 通过控制select 和 poll 的等待时间来实现定时,现在Linux 系统中有了timefd ,可以用和处理IO事件相同的方式来处理定时。

timerfd_create() 函数把时间变为一个文件描述符,该“文件” 在定时器超时的那一刻变得可读,可以融入Reactor 模型中,用同一的方式处理IO 事件和超时事件。

muduo 的定时器功能由三个class实现,TimerId、Timer、 TimerQueue ,用户只能看到TimerId , 另外两个class 是内部实现细节。

TimerQueue 的接口很简单,只有两个函数addTimer() 和 cancel() 。addTimer() 是供给EventLoop 使用的,EventLoop 将其封装为更好的runAt() 、 runAfter() 和 runEvery() 。TimerQueue 的成员函数只能在其所属的IO线程进行调用,因此不需要加锁。

  • TimerQueue 数据结构的选择
    TimerQueue 需要高效地组织尚未到期的Timer, 能快速地根据当前时间找到已经快到期的Timer , 也需要能高效地删除和添加 Timer .一种合适的数据结构是二叉搜索树,把Timer 按照到期时间先后顺序排序,但是不能直接使用map , 因为如果两个同时到期的Timer 应该如何处理?

    • 用multimap 或者multiset
    • 设法区分key , 以pair 为key。这样即使两个Timer 的到期时间相同,他们的地址也必定不同。
	typedef std::pair<Timestamp,Timer*> Entry;
	typedef std::set<Entry> TimerList;  //记录定时器

Muduo网络库的实现TimerQueue定时器(五)_第1张图片

  • 程序代码

==================================TimerQueue.cc ====================================

#define __STD_LIMIT_MACROS
#include "TimerQueue.h"

#include "EventLoop.h"
#include "Timer.h"
#include "TimerId.h"

#include 
#include 

int createTimerfd()
{
	int timerfd = ::timerfd_create(CLOCK_MONOTONIC,TFD_NONBLOCK | TFD_CLOEXEC);

	if(timerfd < 0)
	{
		std::cout << "timerfd_create error ";
		_exit(-1);
	}
	return timerfd;
}

struct timespec howMuchTimeFromNow(Timestamp when)
{	
	int64_t microseconds = when.microSecondsSinceEpoch()-Timestamp::now().microSecondsSinceEpoch();
	if(microseconds < 100) 
	{ microseconds = 100;} 
	struct timespec ts;
	ts.tv_sec = static_cast<time_t>(microseconds / Timestamp::kMicroSecondsPerSecond);//秒
	ts.tv_nsec = static_cast<long>((microseconds % Timestamp::kMicroSecondsPerSecond) * 1000);//纳秒
	return ts;
}

void readTimerfd(int timerfd,Timestamp now)
{
	uint64_t howmany;
	ssize_t n = ::read(timerfd,&howmany,sizeof howmany);
	if(n != sizeof howmany)
	{ std::cout << "TimerQueue::handleRead() reads " << n << " bytes instead of 8";}
}

void resetTimerfd(int timerfd, Timestamp expiration)
{
	struct itimerspec newValue;
	struct itimerspec oldValue;
	bzero(&newValue, sizeof newValue);
	bzero(&oldValue,sizeof oldValue);

	newValue.it_value  = howMuchTimeFromNow(expiration);
	int ret = ::timerfd_settime(timerfd,0,&newValue,&oldValue);
	if(ret)
	{ _exit(-1);}
}


TimerQueue::TimerQueue(EventLoop * loop) 
     : loop_(loop),timerfd_(createTimerfd()),timerfdChannel_(loop,timerfd_),timers_()
{
	timerfdChannel_.setReadCallback(boost::bind(&TimerQueue::handleRead,this));
	timerfdChannel_.enableReading();
}

TimerQueue::~TimerQueue()
{
	::close(timerfd_);
	for(TimerList::iterator it = timers_.begin();it != timers_.end();++it)
	{
		delete it->second;
	}
}


TimerId TimerQueue::addTimer(const TimerCallback & cb,Timestamp when,double interval)
{
	Timer* timer = new Timer(cb,when,interval);
	loop_->assertInLoopThread();
	bool earliestChanged = insert(timer); 

	if(earliestChanged)
	{ resetTimerfd(timerfd_,timer->expiration());}

	return TimerId(timer);
}

void TimerQueue::handleRead()
{
	loop_->assertInLoopThread();
	Timestamp now(Timestamp::now());
	readTimerfd(timerfd_,now);//????

	std::vector<Entry> expired = getExpired(now); 

	for(std::vector<Entry>::iterator it = expired.begin();it != expired.end();++it)
	{
		it->second->run();
	}

	reset(expired,now);
}

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

void TimerQueue::reset(const std::vector<Entry> & expired,Timestamp now)
{
	Timestamp nextExpire;
	for(std::vector<Entry>::const_iterator it  = expired.begin(); it != expired.end();++it)
    {	
	if(it->second->repeat())
	{
		it->second->restart(now);

		insert(it->second);
	}
	else{
		delete it->second;
	}
    }  

	if(!timers_.empty())
	{ nextExpire = timers_.begin()->second->expiration();}

	if(nextExpire.valid())
	{ resetTimerfd(timerfd_,nextExpire);}  
}

	
bool TimerQueue::insert(Timer* timer)
{
  bool earliestChanged = false;
  Timestamp when = timer->expiration();
  TimerList::iterator it = timers_.begin();
  if (it == timers_.end() || when < it->first)
  {
    earliestChanged = true;
  }
  std::pair<TimerList::iterator, bool> result =
          timers_.insert(std::make_pair(when, timer));
  assert(result.second);
  return earliestChanged;
}

==================================TimerQueue.h ====================================

#ifndef _TIMERQUEUE_H
#define _TIMERQUEUE_H
#include 
#include 

#include  

#include "Timestamp.h"
#include "Channel.h"
class EventLoop;
class Timer;
class TimerId;

class TimerQueue : boost::noncopyable
{
  public:
	TimerQueue(EventLoop * loop);
	~TimerQueue();

	TimerId addTimer(const TimerCallback& cb,Timestamp when, double interval);

  private:
	typedef std::pair<Timestamp,Timer*> Entry;
	typedef std::set<Entry> TimerList;  

	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_;
};
#endif

==================================Makefile ====================================

LIB_SRC = Channel.cc EventLoop.cc Poller.cc Timer.cc TimerQueue.cc Timestamp.cc Gettid.cc
BINARIES = test4 
all: $(BINARIES)

CXXFLAGS = -O0 -g  -Wall -I  -pthread

LDFLAGS = -lpthread


$(BINARIES):
	g++ $(CXXFLAGS) -o $@ $(LIB_SRC) $(filter %.cc,$^) $(LDFLAGS)

clean:
	rm -f $(BINARIES) core

test4: test4.cc

Eventloop.cc 文件中需要加的代码,同时需要包含头文件 #include “TimerQueue.h”

TimerId EventLoop::runAt(const Timestamp& time, const TimerCallback& cb)
{
 	 return timerQueue_->addTimer(cb, time, 0.0);
}

TimerId EventLoop::runAfter(double delay, const TimerCallback& cb)
{
 	 Timestamp time(addTime(Timestamp::now(), delay));
 	 return runAt(time, cb);
}

TimerId EventLoop::runEvery(double interval, const TimerCallback& cb)
{
 	 Timestamp time(addTime(Timestamp::now(), interval));
 	 return timerQueue_->addTimer(cb, time, interval);
}

Eventloop.h 文件中需要加的代码,同时需要包含头文件#include “TimerId.h” #include “Timestamp.h” , 还需要向前声明 class TimerQueue, 还有上面从文件中设计的三个函数的声明。

Poller class 代码

Channel class 代码

分类 Muduo网络库编程 学习笔记

本文摘自陈硕老师的linux多线程服务端编程

你可能感兴趣的:(Muduo)