从epoll构建muduo-13 Reactor + ThreadPool 成型
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 成型的更多相关文章
- 从epoll构建muduo-11 单线程Reactor网络模型成型
mini-muduo版本传送门 version 0.00 从epoll构建muduo-1 mini-muduo介绍 version 0.01 从epoll构建muduo-2 最简单的epoll ver ...
- 从epoll构建muduo-12 多线程入场
mini-muduo版本号传送门 version 0.00 从epoll构建muduo-1 mini-muduo介绍 version 0.01 从epoll构建muduo-2 最简单的epoll ve ...
- 从epoll构建muduo-1 mini-muduo介绍
https://blog.csdn.net/voidccc/article/details/8719752 ========== https://blog.csdn.net/liangzhao_jay ...
- Reactor模式解析——muduo网络库
最近一段时间阅读了muduo源码,读完的感受有一个感受就是有点乱.当然不是说代码乱,是我可能还没有完全消化和理解.为了更好的学习这个库,还是要来写一些东西促进一下. 我一边读一边尝试在一些地方改用c+ ...
- Muduo 多线程模型对比
本文主要对比Muduo多线程模型方案8 和方案9 . 方案8:reactor + thread pool ,有一个线程来充当reactor 接受连接分发事件,将要处理的事件分配给thread pool ...
- Muduo网络库实战(二):实现服务器与客户端的连接
1. 方案的确定 1)基本需求 用户1000+, IO压力不大: 多个客户端打开网站,输入查询字符串strclient,发送给服务器=>服务器接收客户端发过来的数据并处理,将结果返回给客户端: ...
- Gradle 1.12 翻译——第十三章 编写构建脚本
有关其它已翻译的章节请关注Github上的项目:https://github.com/msdx/gradledoc/tree/1.12,或訪问:http://gradledoc.qiniudn.com ...
- How To Use Linux epoll with Python
http://scotdoyle.com/python-epoll-howto.html Line 1: The select module contains the epoll functional ...
- python下使用epoll
Reference: http://blog.csdn.net/hehe123456ZXC/article/details/52526670 因为最近想学习如何用epoll写服务器, 于是找到了一篇介 ...
随机推荐
- swal用法
swal({ title: "确认删除?", text: "Your will not be able to recover this imaginary fil ...
- $("[lay-id='"+this.id+"']")
$("[lay-id='"+this.id+"']") $("[lay-id='"+this.id+"'] .layui-tabl ...
- 面包屑 asp代码记录 newsmulu_class 内部函数
'id 这里其实是 classId 'mbStr1 最右边的栏目模板 由于是当前本页面 就不带链接了 建议默认值:<span class='mbxC'>$title</span> ...
- CAD嵌套打印(com接口版)
当用户需要打印两个CAD控件的图纸时,可以采用嵌套打印实现.实现嵌套打印功能,首先将两个CAD控件放入网页中,C#代码如下: private void BatchPrintDialog() { MxD ...
- Java Servlet 非英文乱码
response.setHeader("Content-Type", "text/json; charset=UTF-8"); request.setChara ...
- Q币直充-迅银渠道商(php 面向对象类)
Q币直充的一个类实现 1 <?php /** * DEC : 迅银Q币直充 * User: David Wang * Time: 2018/5/24 下午1:09 */ namespace go ...
- mysql允许远程连接的命令
mysql> use mysql; mysql> GRANT ALL ON *.* TO admin@'%' IDENTIFIED BY 'admin' WITH GRANT OPTION ...
- TortoiseSVN文件夹操作
(1).安装SVN·客户端 (2) 建立库: 1.新建文件夹,目录和文件夹名称最好都用英文,不要使用中文: 2.打开文件夹,在空白处按下“shift键+鼠标右键”: 3.在弹出的菜单中选择“Torto ...
- baidu让用户更快看到首页
//让用户更快看到首页 if(!location.hash.match(/[^a-zA-Z0-9]wd=/)) { document.getElementById("wrapper" ...
- CSU1008: Horcrux
Description A Horcrux is an object in which a Dark wizard or witch has hidden a fragment of his or h ...