Reactor模式
它要求主线程(I/O处理单元,项目中是server类)只负责监听文件描述符上是否有事件发生,有的话就立即通知工作线程(逻辑单元),除此之外,server类(主线程mainLoop)不做任何实质性工作.读写数据,接受新的连接,以及处理客户请求均在工作线程中完成
工作流程(使用同步I/O模型)
- one loop per thread
- 创建threadnum个大小的线程
- 主线程创建监听描述符,在server类中实现了连接回调,将接受到的连接绑定到epoll事件列表中
- 新的连接将分配一个eventloop给其使用,实现one loop per thread
- 监听连接的数据可读事件
EventLoop
1.基于muduo中one loop per thread的思想,我们需要创建一个线程池,给新到来的每个连接run一个Eventloop, 在构造函数中,需要检查当前thread是否已经创建了其他EventLoop对象
- 在IO线程中执行用户任务回调,EventLoop::runInLoop(const Fucntor &cb);如果用户在其他线程中执行调用runInLoop,cb会加入队列,IO线程会被唤醒来执行这个functor
2.1 因为IO线程平时会阻塞在IO复用函数epoll_wait的调用当中,为了能让IO线程立即执行用户回调, 通过设立wakeupFd_来唤醒IO线程
2.2 doPendingFuctor()中通过把回调列表swap()到局部变量functors中: 1.减小临界区长度(意味着不会阻塞其他线程调用queueInLoop()来往pendingFunctors中添加新的回调); 2.避免了死锁(添加回调时可能会再调用queueInLoop, 而对于pendingFunctors需要加锁操作)
Epoller
- IO复用的封装,其owner EventLoop在IO线程中调用,无需加锁。生命期与EventLoop相等。Epoller不拥有Channel,Channel在析构的时候必须自己unregister,避免出现空悬指针
- 不能一边Epoller::poll()一边Channel::handleEvents(),因为后者会添加或者删除Channel,从而造成其在poll的过程中出现Channellist大小改变的情况;让Epoller::poll()只负责IO复用,不负责事件分发(dispatching)
Channel
- 每个Channel从属于一个EventLoop中,在构造函数中通过绑定EventLoop和connfd来构建一个新的Channel,负责该connfd的IO事件的分发
- events_是channel关心的IO事件,由用户设置
- revents_是目前活动的事件,由EventLoop/Epoller设置
- Channel并不拥有该connfd,在析构时也不会关闭该connfd
网友评论