说明:本文参照muduo代码,主要用意是简化muduo代码呈现其主要结构,并脱离muduo的文件依赖。

本节简化的是Reactor的关键结构部分:EventLoop、Poller、Channel。遵照one loop per thread原则,一个事件循环对应一个IO线程,IO线程运行EventLoop事件主循环,该主循环loop调用IO复用器Poller监听事件集合,并将就绪事件通过事件分发器Channel执行相应的事件回调。通过C++封装的代码如下,名为Reactor.hpp(需将测试代码注释掉):

  1. #include<iostream>
  2. #include<stdio.h>
  3. #include<vector>
  4. #include<map>
  5. #include<poll.h>
  6. #include<boost/noncopyable.hpp>
  7. #include<boost/function.hpp>
  8. #include<assert.h>
  9. #include<boost/scoped_ptr.hpp>
  10. #include<stdlib.h>
  11. #include<sys/syscall.h>
  12. #include<pthread.h>
  13. #include<unistd.h>
  14. #include<string.h>
  15. using namespace std;
  16. using namespace boost;
  17. //以下类都是在同一个IO线程中运行为线程安全的故不需要同步机制
  18.  
  19. class Channel;//前向声明,事件分发器主要用于事件注册与事件处理(事件回调)
  20. class Poller;//IO复用机制,主要功能是监听事件集合,即select,poll,epoll的功能
  21.  
  22. //事件循环,一个线程一个事件循环即one loop per thread,其主要功能是运行事件循环如等待事件发生然后处理发生的事件
  23. class EventLoop:noncopyable{
  24. public:
  25. EventLoop();
  26. ~EventLoop();
  27. void loop();//事件循环主体
  28. void quit();//终止事件循环,通过设定标志位所以有一定延迟
  29. void updateChannel(Channel* channel);//更新事件分发器Channel,完成文件描述符fd向事件集合注册事件及事件回调函数
  30. void assertInLoopThread(){//若运行线程不拥有EventLoop则退出,保证one loop per thread
  31. if(!isInLoopThread()){
  32. abortNotInLoopThread();
  33. }
  34. }
  35. bool isInLoopThread() const{return threadID_==syscall(SYS_gettid);}//判断运行线程是否为拥有此EventLoop的线程
  36. private:
  37. void abortNotInLoopThread();//在不拥有EventLoop线程中终止
  38. typedef vector<Channel*> ChannelList;//事件分发器Channel容器,一个Channel只负责一个文件描述符fd的事件分发
  39. bool looping_;//事件循环主体loop是运行标志
  40. bool quit_;//取消循环主体标志
  41. const pid_t threadID_;//EventLoop的附属线程ID
  42. scoped_ptr<Poller> poller_;//IO复用器Poller用于监听事件集合
  43. ChannelList activeChannels_;//类似与poll的就绪事件集合,这里集合换成Channel(事件分发器具备就绪事件回调功能)
  44. };
  45.  
  46. //IO Multiplexing Poller即poll的封装,主要完成事件集合的监听
  47. class Poller:noncopyable{
  48. public:
  49. typedef vector<Channel*> ChannelList;//Channel容器(Channel包含了文件描述符fd和fd注册的事件及事件回调函数),Channel包含文件描述符及其注册事件及其事件回调函数,这里主要用于返回就绪事件集合
  50. Poller(EventLoop* loop);
  51. ~Poller();
  52. void Poll(int timeoutMs,ChannelList* activeChannels);//监听事件集合,通过activeChannels返回就绪事件集合
  53. void updateChannel(Channel* channel);//向事件集合中添加描述符欲监听的事件(channel中含有fd及其事件)
  54. void assertInLoopThread(){//判定是否和EventLoop的隶属关系,EventLoop要拥有此Poller
  55. ownerLoop_->assertInLoopThread();
  56. }
  57. private:
  58. void fillActiveChannels(int numEvents,ChannelList* activeChannels) const;//将就绪的事件添加到activeChannels中用于返回就绪事件集合
  59. typedef vector<struct pollfd> PollFdList;//struct pollfd是poll系统调用监听的事件集合参数
  60. typedef map<int,Channel*> ChannelMap;//文件描述符fd到IO分发器Channel的映射,通过fd可以快速找到Channel
  61. //注意:Channel中有fd成员可以完成Channel映射到fd的功能,所以fd和Channel可以完成双射
  62. EventLoop* ownerLoop_;//隶属的EventLoop
  63. PollFdList pollfds_;//监听事件集合
  64. ChannelMap channels_;//文件描述符fd到Channel的映射
  65. };
  66.  
  67. //事件分发器该类包含:文件描述符fd、fd欲监听的事件、事件的处理函数(事件回调函数)
  68. class Channel:noncopyable{
  69. public:
  70. typedef function<void()> EventCallback;//事件回调函数类型,回调函数的参数为空,这里将参数类型已经写死了
  71. Channel(EventLoop* loop,int fd);//一个Channel只负责一个文件描述符fd但Channel不拥有fd,可见结构应该是这样的:EventLoop调用Poller监听事件集合,就绪的事件集合元素就是Channel。但Channel的功能不仅是返回就绪事件,还具备事件处理功能
  72. void handleEvent();//处理事件回调
  73. void setReadCallBack(const EventCallback& cb){//可读事件回调
  74. readCallback=cb;
  75. }
  76. void setWriteCallback(const EventCallback& cb){//可写事件回调
  77. writeCallback=cb;
  78. }
  79. void setErrorCallback(const EventCallback& cb){//出错事件回调
  80. errorCallback=cb;
  81. }
  82. int fd() const{return fd_;}//返回Channel负责的文件描述符fd,即建立Channel到fd的映射
  83. int events() const{return events_;}//返回fd域注册的事件类型
  84. void set_revents(int revt){//设定fd的就绪事件类型,再poll返回就绪事件后将就绪事件类型传给此函数,然后此函数传给handleEvent,handleEvent根据就绪事件的类型决定执行哪个事件回调函数
  85. revents_=revt;
  86. }
  87. bool isNoneEvent() const{//fd没有想要注册的事件
  88. return events_==kNoneEvent;
  89. }
  90. void enableReading(){//fd注册可读事件
  91. events_|=kReadEvent;
  92. update();
  93. }
  94. void enableWriting(){//fd注册可写事件
  95. events_|=kWriteEvent;
  96. update();
  97. }
  98. int index(){return index_;}//index_是本Channel负责的fd在poll监听事件集合的下标,用于快速索引到fd的pollfd
  99. void set_index(int idx){index_=idx;}
  100. EventLoop* ownerLoop(){return loop_;}
  101. private:
  102. void update();
  103. static const int kNoneEvent;//无任何事件
  104. static const int kReadEvent;//可读事件
  105. static const int kWriteEvent;//可写事件
  106.  
  107. EventLoop* loop_;//Channel隶属的EventLoop(原则上EventLoop,Poller,Channel都是一个IO线程)
  108. const int fd_;//每个Channel唯一负责的文件描述符,Channel不拥有fd
  109. int events_;//fd_注册的事件
  110. int revents_;//通过poll返回的就绪事件类型
  111. int index_;//在poll的监听事件集合pollfd的下标,用于快速索引到fd的pollfd
  112. EventCallback readCallback;//可读事件回调函数,当poll返回fd_的可读事件时调用此函数执行相应的事件处理,该函数由用户指定
  113. EventCallback writeCallback;//可写事件回调函数
  114. EventCallback errorCallback;//出错事件回调函数
  115. };
  116.  
  117. /*
  118. *EventLoop成员实现
  119. */
  120. __thread EventLoop* t_loopInThisThread=0;//线程私有数据表示线程是否拥有EventLoop
  121. const int kPollTimeMs=10000;//poll等待时间
  122. EventLoop::EventLoop():looping_(false),quit_(false),threadID_(syscall(SYS_gettid)),poller_(new Poller(this)){
  123. if(!t_loopInThisThread){
  124. t_loopInThisThread=this;//EventLoop构造时线程私有数据记录
  125. }
  126. }
  127. EventLoop::~EventLoop(){
  128. assert(!looping_);
  129. t_loopInThisThread=NULL;//EventLoop析构将其置空
  130. }
  131. void EventLoop::loop(){//EventLoop主循环,主要功能是监听事件集合,执行就绪事件的处理函数
  132. assert(!looping_);
  133. assertInLoopThread();
  134. looping_=true;
  135. quit_=false;
  136. while(!quit_){
  137. activeChannels_.clear();
  138. poller_->Poll(kPollTimeMs,&activeChannels_);//activeChannels是就绪事件
  139. for(ChannelList::iterator it=activeChannels_.begin();it!=activeChannels_.end();it++){
  140. (*it)->handleEvent();//处理就绪事件的回调函数
  141. }
  142. }
  143. looping_=false;
  144. }
  145. void EventLoop::quit(){
  146. quit_=true;//停止主循环标志,主循环不会马上停止有延迟
  147. }
  148. void EventLoop::updateChannel(Channel* channel){//主要用于文件描述符添加到poll的监听事件集合中
  149. assert(channel->ownerLoop()==this);
  150. assertInLoopThread();
  151. poller_->updateChannel(channel);
  152. }
  153. void EventLoop::abortNotInLoopThread(){
  154. printf("abort not in Loop Thread\n");
  155. abort();//非本线程调用强行终止
  156. }
  157. /*
  158. *Poller成员实现
  159. */
  160. Poller::Poller(EventLoop* loop):ownerLoop_(loop){}//Poller明确所属的EventLoop
  161. Poller::~Poller(){}
  162. void Poller::Poll(int timeoutMs,ChannelList* activeChannels){
  163. int numEvents=poll(&*pollfds_.begin(),pollfds_.size(),timeoutMs);//poll监听事件集合pollfds_
  164. if(numEvents>0){
  165. fillActiveChannels(numEvents,activeChannels);//将就绪的事件添加到activeChannels
  166. }
  167. else if(numEvents==0){
  168. }
  169. else{
  170. printf("Poller::Poll error\n");
  171. }
  172. }
  173. void Poller::fillActiveChannels(int numEvents,ChannelList* activeChannels) const{//将就绪事件通过activeChannels返回
  174. for(PollFdList::const_iterator pfd=pollfds_.begin();pfd!=pollfds_.end()&&numEvents>0;++pfd){
  175. if(pfd->revents>0){
  176. --numEvents;//若numEvents个事件全部找到就不需要再遍历容器剩下的部分
  177. ChannelMap::const_iterator ch=channels_.find(pfd->fd);
  178. assert(ch!=channels_.end());
  179. Channel* channel=ch->second;
  180. assert(channel->fd()==pfd->fd);
  181. channel->set_revents(pfd->revents);
  182. activeChannels->push_back(channel);
  183. }
  184. }
  185. }
  186. void Poller::updateChannel(Channel* channel){
  187. assertInLoopThread();
  188. if(channel->index()<0){//若channel的文件描述符fd没有添加到poll的监听事件集合中
  189. assert(channels_.find(channel->fd())==channels_.end());
  190. struct pollfd pfd;
  191. pfd.fd=channel->fd();
  192. pfd.events=static_cast<short>(channel->events());
  193. pfd.revents=0;
  194. pollfds_.push_back(pfd);
  195. int idx=static_cast<int>(pollfds_.size())-1;
  196. channel->set_index(idx);
  197. channels_[pfd.fd]=channel;
  198. }
  199. else{//若已经添加到监听事件集合中,但是需要修改
  200. assert(channels_.find(channel->fd())!=channels_.end());
  201. assert(channels_[channel->fd()]==channel);
  202. int idx=channel->index();
  203. assert(0<=idx&&idx<static_cast<int>(pollfds_.size()));
  204. struct pollfd& pfd=pollfds_[idx];
  205. assert(pfd.fd==channel->fd()||pfd.fd==-1);
  206. pfd.events=static_cast<short>(channel->events());//修改注册事件类型
  207. pfd.revents=0;
  208. if(channel->isNoneEvent()){
  209. pfd.fd=-1;//若无事件则poll忽略
  210. }
  211. }
  212. }
  213. /*
  214. *Channel成员实现
  215. */
  216. const int Channel::kNoneEvent=0;//无事件
  217. const int Channel::kReadEvent=POLLIN|POLLPRI;//可读事件
  218. const int Channel::kWriteEvent=POLLOUT;//可写事件
  219. Channel::Channel(EventLoop* loop,int fdArg):loop_(loop),fd_(fdArg),events_(0),revents_(0),index_(-1){}
  220. void Channel::update(){//添加或修改文件描述符的事件类型
  221. loop_->updateChannel(this);
  222. }
  223. void Channel::handleEvent(){//处理就绪事件的处理函数
  224. if(revents_&POLLNVAL){
  225. printf("Channel::handleEvent() POLLNVAL\n");
  226. }
  227. if(revents_&(POLLERR|POLLNVAL)){//出错回调
  228. if(errorCallback)
  229. errorCallback();
  230. }
  231. if(revents_&(POLLIN|POLLPRI|POLLRDHUP)){//可读回调
  232. if(readCallback)
  233. readCallback();
  234. }
  235. if(revents_&POLLOUT){//可写回调
  236. if(writeCallback)
  237. writeCallback();
  238. }
  239. }
  240. /*
  241. *测试代码,主线程往管道写数据,子线程通过EventLoop监听管道读端然后执行相应的可读回调(读取数据并输出)
  242. */
  243. int pipefd[2];
  244. EventLoop* loop;
  245. void ReadPipe(){
  246. char buf[1024];
  247. read(pipefd[0],buf,1024);
  248. printf("%s\n",buf);
  249. loop->quit();//执行完可读回调后终止EventLoop的事件循环loop
  250. }
  251. void* threadFun(void* arg){
  252. loop=new EventLoop();
  253. Channel channel(loop,pipefd[0]);
  254. channel.setReadCallBack(ReadPipe);
  255. channel.enableReading();
  256. loop->loop();
  257. }
  258. int main(){
  259. pthread_t pid;
  260. pipe(pipefd);
  261. pthread_create(&pid,NULL,&threadFun,NULL);
  262. const char* ptr="Can you get this data?";
  263. write(pipefd[1],ptr,strlen(ptr));
  264. pthread_join(pid,NULL);
  265. return 0;
  266. }

测试代码的输出:

Can you get this data?

muduo简化(1):Reactor的关键结构的更多相关文章

  1. muduo学习笔记(二)Reactor关键结构

    目录 muduo学习笔记(二)Reactor关键结构 Reactor简述 什么是Reactor Reactor模型的优缺点 poll简述 poll使用样例 muduo Reactor关键结构 Chan ...

  2. muduo源代码分析--Reactor模式在muduo中的使用

    一. Reactor模式简单介绍 Reactor释义"反应堆",是一种事件驱动机制.和普通函数调用的不同之处在于:应用程序不是主动的调用某个API完毕处理.而是恰恰相反.React ...

  3. ARM-Linux S5PV210 UART驱动(3)----串口核心层、关键结构体、接口关系

    尽管一个特定的UART设备驱动完全可以按照tty驱动的设计方法来设计,即定义tty_driver并实现tty_operations其中的成员函数,但是Linux已经在文件serial_core.c中实 ...

  4. muduo源码分析:组成结构

    muduo整体由许多类组成: 这些类之间有一些依赖关系,如下:

  5. Framebuffer 驱动学习总结(一) ---- 总体架构及关键结构体

    一.Framebuffer 设备驱动总体架构 帧缓冲设备为标准的字符型设备,在Linux中主设备号29,定义在/include/linux/major.h中的FB_MAJOR,次设备号定义帧缓冲的个数 ...

  6. muduo源代码分析--Reactor在模型muduo使用(两)

    一. TcpServer分类: 管理所有的TCP客户连接,TcpServer对于用户直接使用,直接控制由用户生活. 用户只需要设置相应的回调函数(消息处理messageCallback)然后TcpSe ...

  7. Linux多线程服务端编程:使用muduo C++网络库

    内容推荐本 书主要讲述采用现代C++在x86-64 Linux上编写多线程TCP网络服务程序的主流常规技术,重点讲解一种适应性较强的多线程服务器的编程模型,即one loop per thread.这 ...

  8. Mudo C++网络库第八章学习笔记

    muduo网络库的设计与实现 muduo是基于Reactor模式的C++网络库; Reactor的关键结构 Reactor最核心的是事件分发机制, 即将IO multiplexing拿到IO事件分发给 ...

  9. Linux多线程服务器端编程

    目录 Linux多线程服务器端编程 线程安全的对象生命期管理 对象的销毁线程比较难 线程同步精要 借shared_ptr实现写时拷贝(copy-on-write) 多线程服务器的适用场合与常用编程模型 ...

随机推荐

  1. Oracle SQL CPU占用高

    Oracle数据库经常会遇到CPU利用率很高的情况,这种时候大都是数据库中存在着严重性能低下的SQL语句,这种SQL语句大大的消耗了CPU资源,导致整个系统性能低下.当然,引起严重性能低下的SQL语句 ...

  2. 教你看懂C++类库函数定义之二---STDMETHOD介绍

    一切从一个C++ 类库头文件开始,现在在做一个C++的项目,期间用到一个开源的界面库DUILib(类似MFC),这个东西还不错能很容易的写出漂亮的界面,比如QQ的界面,可以去下载下来研究研究,地址:h ...

  3. c# winform读取xml创建菜单

    动态创建菜单使得程序灵活性大大增加,本文根据读取xml文件中的配置菜单项来动态创建菜单,代码如下: using System; using System.Collections.Generic; us ...

  4. A20 GPIO中断类型差别结果迥异的问题思考

    A20GPIO中断类型差别结果迥异的问题思考 最近在使用全志A20做开发时,发现在处理中断的时候,用电平触发模式,报中断比较乱,用边沿触发则很稳定,不会乱报.笔者感到比较困惑,笔者用电平触发写的cod ...

  5. C++读写文件流的相关介绍

    掌握文本文件读写的方法了解二进制文件的读写方法 C++文件流:fstream // 文件流ifstream  // 输入文件流ofstream  // 输出文件流 //创建一个文本文件并写入信息//同 ...

  6. UVA-Matrix Chain Multiplication(栈)

     Matrix Chain Multiplication  Suppose you have to evaluate an expression like A*B*C*D*E where A,B,C, ...

  7. 第一章 什么是SQL Server Integration Services (ssis) 系统。

    note:我也是刚入门的菜鸟,让我们大家一块学习SSIS系统,工作中需要用到SSIS.您的浏览是我更新的最大动力,谢谢!  SSIS是Microsoft SQL Server Integration ...

  8. Jquery filter()方法简介

    利用filter函数可以从wrapper set中过滤符合条件的dom元素. 如下图html代码,假如我们要获取类名为filter的<a>标签,用filter方法可以很轻松的获得. < ...

  9. 关于SQL Server数据表的五中约束

    1.主键约束(PRIMARY KEY) 主键约束可以在表中定义一个主键值,它可以唯一确定表中每一条记录,每个表中只能有一个主键约束(只能有一个主键约束的意思并不是说受主键约束的列只能有一个),并且受主 ...

  10. hough变换中,直线方程从XY空间转换到参数空间的转换过程

    XY空间直线方程:y=kx+b 参数空间直线方程:xcosθ+ysinθ=ρ 直线方程从XY空间转换到参数空间过程的转换过程: k=tan(π-α)=tan(-α)=-tanα=-cotθ=-cosθ ...