muduo网络库net篇一:事件处理

时间有限,net篇采用边看代码边写。
net部分的类都定义在muduo::net命名空间下。

EventLoop类

这个类涉及好几个类:

 boost::scoped_ptr<Poller> poller_;
 boost::scoped_ptr<TimerQueue> timerQueue_;
 boost::scoped_ptr<Channel> wakeupChannel_;

 ChannelList activeChannels_;
 Channel* currentActiveChannel_;

EventLoop类有其类对于操作的API,这里不讲,等到阅读到对应代码时再具体分析。

首先EventLoop类构造函数中有:

  wakeupChannel_->setReadCallback(
      boost::bind(&EventLoop::handleRead, this));
  wakeupChannel_->enableReading();

这个肯定是注册epoll或poll监听的事件,事件到达就可唤醒当前线程。
事件对象wakeupFd_实际为:

evtfd = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);

read evtfd会读取8字节值, 并把计数器重设为0

EventLoop类还有一些状态成员,用来同步操作,不赘述。

操作流程大致为:
当前线程创建EventLoop对象,假设为loop。
之后可以设置监听事件,可以注册定时器函数,也可以注册到pendingFunctors_,因为loop在一个线程中是唯一的,只能创建一个。所以这些操作需要在调用loop前完成,主逻辑在loop函数中且是循环执行。

跨线程可以loop.runInLoop(…)把调度函数放到pendingFunctors_队列中,并唤醒事件,也就是向wakeupFd_写操作——计数器加1;可以run…() 注册定时器事件;也可以quit停止loop;其余操作目前看来不可以。

Poller类

Poller类是PollPoller类和EPollPoller类的父类,成员数据:

 protected:
  typedef std::map<int, Channel*> ChannelMap;
  ChannelMap channels_;

 private:
  EventLoop* ownerLoop_;

由此可见Poller是和EventLoop关联的,虚函数如下:

  virtual ~Poller();
  virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels) = 0;
  virtual void updateChannel(Channel* channel) = 0;
  virtual void removeChannel(Channel* channel) = 0;
  virtual bool hasChannel(Channel* channel) const;

其中hasChannel()已在Poller类中实现

Poller类中newDefaultPoller函数会生成一个默认对象,此函数单独定义在一个文件中。

在阅读代码时,可结合I/O 多路复用技术看。

PollPoller类和EPollPoller类除了本身封装linux API外,最重要的是对channels_的操作。
这是个Map,保存监听对象的fd与Channel对应关系

Channel类

事件分为无、读、写三种:

const int Channel::kNoneEvent = 0;
const int Channel::kReadEvent = POLLIN | POLLPRI;
const int Channel::kWriteEvent = POLLOUT;

Channel类中重要的成员数据有:

  EventLoop* loop_;
  const int  fd_;
  int        events_;
  int        revents_; // it's the received event types of epoll or poll
  int        index_; // used by Poller.

成员函数handleEvent完成事件处理,函数原型:

void handleEvent(Timestamp receiveTime);

小结

上面主要讲了三个东西:
EventLoop:事件循环——我把发生的事件一一处理
Poller:I/O多路复用——我来监督事件有没有发生
Channel:事件——我来决定I/O事件,并告诉你发生后该干什么

从最基本谈起:
1、要创建Poller对象
而创建Poller对象的构造函数表示需要告知隶属于哪个事件循环,于是需要创建EventLoop对象
创建EventLoop对象意味着要创建其成员Poller对象和Channel对象
2、需要注册事件,即调用Poller.updateChannel(Channel* channel)
而这必须先创建Channel对象
3、等待事件发生,即调用Poller.poll(int timeoutMs, ChannelList* activeChannels)
4、事件处理,即调用Channel.handleEvent(Timestamp receiveTime)
5、循环3、4

上述的3、4都是在EventLoop.loop()中完成的,这意味着EventLoop.poller_即是1中想要创建的对象。
于是实际情况就变成了:
1、创建EventLoop对象
2、创建Channel对象
3、调用EventLoop.updateChannel()注册事件
4、调用EventLoop.loop()

还有两个小问题
1、EventLoop.wakeupChannel_是如何注册的?
在构造函数时即完成了。实际上wakeupChannel_(evtfd的读事件)绕了一大圈
wakeupChannel_.enableReading()
wakeupChannel_.update()
loop_->updateChannel(this);
又回来了完成注册。

2、既然EventLoop.loop()是循环,且EventLoop对象一个线程中只能创建一个,事件状态变更又必须是当前线程,如何做到变更或停止呢?
我认为只能由其他线程quit()停止之后再完成更新。待验证。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值