源码下载以及安装点击链接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
typedef std::pair<Timestamp,Timer*> Entry;
typedef std::set<Entry> TimerList; //记录定时器
==================================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多线程服务端编程