mini-muduo版本号传送门

version 0.00 从epoll构建muduo-1 mini-muduo介绍

version 0.01 从epoll构建muduo-2 最简单的epoll

version 0.02 从epoll构建muduo-3 增加第一个类,顺便介绍Reactor

version 0.03 从epoll构建muduo-4 增加Channel

version 0.04 从epoll构建muduo-5 增加Acceptor和TcpConnection

version 0.05 从epoll构建muduo-6 增加EventLoop和Epoll

version 0.06 从epoll构建muduo-7 增加IMuduoUser

version 0.07 从epoll构建muduo-8 增加发送缓冲区和接收缓冲区

version 0.08 从epoll构建muduo-9 增加onWriteComplate回调和Buffer

version 0.09 从epoll构建muduo-10 Timer定时器

version 0.11 从epoll构建muduo-11 单线程Reactor网络模型成型

version 0.12 从epoll构建muduo-12 多线程代码入场

version 0.13 从epoll构建muduo-13 Reactor + ThreadPool 成型

mini-muduo v0.13版本号,mini-muduo完整可执行演示样例可从github下载,使用命令git checkout v0.13可切换到此版本号,在线浏览此版本号到这里

本版是个里程碑版本号。能够通过本版了解多线程是怎样通过IO线程读/写网络数据的,在前一个版本号v0.12重点介绍了基础知识的前提下,本篇着重分析多线程逻辑里最重要的三个方法EventLoop::runInLoop/EventLoop::queueInLoop/EventLoop::doPendingFunctors。以下逐步介绍本版本号改动的细节。三个方法放在最后的EventLoop节。

1 Task类

这个类是v0.13版本号新增加的,它就是一个携带參数的回调。它的作用就是闭包(closure),它是我们用来取代muduo里boost::function和boost:bind的。为什么不使用boost::function和boost:bind?之前解释过了。为了不引入新概念。减少mini-muduo的学习成本。这个Task类不太具有通用性(不像BlockingQueue,范型实现)。仅仅是为了在本项目里使用的。Task仅仅支持两种类型的回调,第一种是无參数的回调。被调用者仅仅须要实现一个”void run0()“。另外一种是有两个參数的回调,被调用者实现"void
run2(const string&, void*)"。

由于有了Task类,全部须要异步回调的地方都用它来实现了。

2 TcpConnection

加入了一个sendInLoop方法。把原来send方法里的实现移动到了sendInLoop方法里,而send方法本身变成了一个外部接口的包装。依据调用send方法所在线程的不同,採取不同的策略,假设调用TcpConnection::send的线程刚好是IO线程。则立马使用write将数据送出(当然是缓冲区为空的时候)。假设调用TcpConnection::send的线程是work线程(也就是后台处理线程)则仅仅将要发送的信息通过Task对象丢到EventLoop的异步队列中,然后立马返回。

EventLoop随后会在IO线程回调到TcpConnetion::sendInLoop方法,这样做的目的是保证网络IO相关操作仅仅在IO线程进行。

3 TimerQueue

修改不大,仅仅是用Task包装了异步请求。这样保证全部关于Timer的操作都在IO线程进行。由于我们就是用timerfd来实现的定时器,而timerfd又是由epoll来监控的,所以这非常好理解,对epoll监控的全部文件描写叙述的操作都要放到IO线程。

4 EchoServer

在接到任务后不是立马处理,而是将任务通过ThreadPool::addTask丢进线程池,使用多线程处理,在真正的处理回调里,简单的模拟了一个消耗CPU的函数(计算斐波那契数列),通过log能够看到。每次的任务都被分配给了池子里的不同线程。

5 EventLoop

1 wakeup方法的实如今上一版本号v0.12已经增加。可是调用被凝视掉了。这次调用点位于EventLoop::queueInLoop。这种方法是用唤醒IO线程的,确切的说是唤醒IO线程里的epoll_wait。仅仅有一点要注意,别忘记在EventLoop::handleRead里读出这个uint_64,否则会导致eventfd被持续激发使程序进入无限循环。

2 EventLoop::queueInLoop方法,这种方法在v0.12版本号叫queueLoop,为了和原始muduo保持一致,本版改名了。这种方法的作用是将一个异步回调加入到待运行队列_pendingFunctors中。与v0.12版本号相比第一个差异是本版本号对_pendingFunctors加了锁,这点非常好理解,由于EventLoop::queueLoop常常被外部的其它非IO线程调用。第二个改动是加入了一定条件下的wakeup()唤醒。为什么单线程版本号没有这个唤醒逻辑?由于单线程版本号里全部的异步调用都是在Loop循环開始后,doPendingFunctors()之前触发的,仅仅须要把回调插入_pendingFunctors这个数组就可以。可是在多线程版本号queueInLoop的入口就非常多了,比方以下这3种情况下,都可能调用EventLoop::queueInLoop

情况 1 IO线程。在IMuduoUser::onMessage回调里。比方EchoServer::onMessage里。

情况 2 IO线程,在doPendingFunctors()运行Task->doTask的实现体里。比方EchoServer::onWriteComplate里。

情况 3 非IO线程,线程池的还有一个线程中。

在单线程版本号里,能够不考虑情况3。情况2尽管有可能发生。可是我们当时简单如果用户仅仅会在onMessage加入Task,而不会在Task的回调里再加入Task。

所以上一个版本号在此处进行了简单化处理,并不须要wakeup()操作。

本版本号因为要考虑这几种情况,所以加入了一些条件推断和wakeup()调用。

特别要注意_callingPendingFunctors变量。这个变量有点隐晦。我開始在敲代码的时候忽略了它,后来发现它很重要,在上面的情况2时,假设没有这个变量,会导致异步调用永远不会触发!

3 EventLoop::runInLoop方法,本版本号新加入的方法,与queueInLoop方法很相似,"runIn"和"queueIn"从名字的差异就能够理解,当外部调用runInLoop的时候,会推断当前是否为IO线程。假设是在IO线程,则立马运行Task里的回调。否则通过调用queueInLoop将Task加入到异步队列,等待兴许被调用。

4 EventLoop::doPendingFunctors。这种方法与queueInLoop方法一样,也是两处改动。首先是因为多线程操作vector必需要加锁,另外是加入了_callingPendingFunctors变量的控制。再次强调这个变量很重要。

本篇的最后。为了更清晰的解释EventLoop在多线程环境下的逻辑,我画了一张时序图。时序图表达的就是“在非IO线程里调用TcpConnection::send发送数据”这一动作引发的连锁调用。

这一动作须要3个Loop来完毕,涉及4个子调用过程。

绿色表明代码工作在IO线程红色表示代码工作在Work线程(工作线程。真正处理计算任务的线程)。在原书中多线程EventLoop的解说位于294页附近。可是非常遗憾,作者没有为这一过程制作时序图。我这里算是补画一张吧。

从epoll构建muduo-13 Reactor + ThreadPool 成型的更多相关文章

  1. 从epoll构建muduo-11 单线程Reactor网络模型成型

    mini-muduo版本传送门 version 0.00 从epoll构建muduo-1 mini-muduo介绍 version 0.01 从epoll构建muduo-2 最简单的epoll ver ...

  2. 从epoll构建muduo-12 多线程入场

    mini-muduo版本号传送门 version 0.00 从epoll构建muduo-1 mini-muduo介绍 version 0.01 从epoll构建muduo-2 最简单的epoll ve ...

  3. 从epoll构建muduo-1 mini-muduo介绍

    https://blog.csdn.net/voidccc/article/details/8719752 ========== https://blog.csdn.net/liangzhao_jay ...

  4. Reactor模式解析——muduo网络库

    最近一段时间阅读了muduo源码,读完的感受有一个感受就是有点乱.当然不是说代码乱,是我可能还没有完全消化和理解.为了更好的学习这个库,还是要来写一些东西促进一下. 我一边读一边尝试在一些地方改用c+ ...

  5. Muduo 多线程模型对比

    本文主要对比Muduo多线程模型方案8 和方案9 . 方案8:reactor + thread pool ,有一个线程来充当reactor 接受连接分发事件,将要处理的事件分配给thread pool ...

  6. Muduo网络库实战(二):实现服务器与客户端的连接

    1. 方案的确定 1)基本需求 用户1000+, IO压力不大: 多个客户端打开网站,输入查询字符串strclient,发送给服务器=>服务器接收客户端发过来的数据并处理,将结果返回给客户端: ...

  7. Gradle 1.12 翻译——第十三章 编写构建脚本

    有关其它已翻译的章节请关注Github上的项目:https://github.com/msdx/gradledoc/tree/1.12,或訪问:http://gradledoc.qiniudn.com ...

  8. How To Use Linux epoll with Python

    http://scotdoyle.com/python-epoll-howto.html Line 1: The select module contains the epoll functional ...

  9. python下使用epoll

    Reference: http://blog.csdn.net/hehe123456ZXC/article/details/52526670 因为最近想学习如何用epoll写服务器, 于是找到了一篇介 ...

随机推荐

  1. UI开发复杂度度量

    1)要素的个数: 2)要素布局和渲染的复杂度: 3)交互的复杂度. 本质上分为两种:要素的复杂度和联系的复杂度. 联系包含要素间布局的联系与交互的联系,已经和外部上下文的联系.

  2. WPS 常用操作

    1.WPS屏保太美了,如何保存 网上搜到如下资料,发现可以在电脑中找到若干个被缓存的图片,kwallpaper可能为kscreensaver

  3. CREATE GROUP - 定义一个新的用户组

    SYNOPSIS CREATE GROUP name [ [ WITH ] option [ ... ] ] where option can be: SYSID gid | USER usernam ...

  4. mysql安装及navicat连接

    1.下载mysql官方连接:https://dev.mysql.com/downloads/mysql/ 下载成功后,解压到自己想要的路径下并创建my.ini文件和配置环境变量 然后我们在根目录下创建 ...

  5. python 弹窗

    import ctypes message = ctypes.windll.user32.MessageBoxA(0,'message','tips',0)

  6. jquery toggle()设置

    很多朋友对jquery toggle()比较熟练,甚至经常用到,而且对toggle的三个参数也比较了解$(selector).toggle(speed,callback,switch).但是当你设置$ ...

  7. [SCOI2011]棘手的操作(可并堆/并查集/线段树)

    我懒死了 过于棘手 但这题真的很水的说 毕竟写啥都能过 常见思路: ①:由于不强制在线,所以重新编号之后线段树维护 ②:用各种可以高速合并的数据结构,比如可并堆,可并平衡树啥的 讲一种无脑算法: 对于 ...

  8. Ztree勾选节点后取消勾选其父子节点

    前言: Ztree官方给的API可以设置勾选一个节点的同时勾选子节点或者父节点,也可以设置不影响父子节点,即将chkboxType设置为{"Y":"",&quo ...

  9. jq ajax请求error: Maximum call stack size exceeded

    原因是data中参数iconUrl这个变量未声明导致的.jq在内部循环时报错

  10. Matlab学习笔记(二)

    二.MATLAB基础知识 (二)数值.变量和表达式 命名规则: 变量名对大小写敏感,即区分大小写 变量名必须以字母开头,后面可以采用数字.下划线和字母,但不能使用空格.标点符号和运算符 变量名最长可以 ...