从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写服务器, 于是找到了一篇介 ...
随机推荐
- 使用Gson解析Json数组遇到的泛型类型擦除问题解决方法
谷歌Gson转换Json串有如下方法: public Object fromJson(String json, Type typeOfT);1可以使用它进行数组解析.如下,使用此方法解析Json串为类 ...
- jQuery 点击 星星评分
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 类unix系统 递归删除指定文件
递归删除当前目录下所有以 ._开头的文件 find . -name "._*" | xargs rm -f 或者: find . -name "._*" -ex ...
- java.util.MissingResourceException: Can't find bundle for base name db, locale zh_CN
在使用Bundle来加载配置文件的时候, 爆出了这个错误: 原因? 没有找到需要加载的配置文件,因为配置文件必须放在src目录下面, 如果放进了com.bj186.crm的包下面,就必须添加包的名称到 ...
- 根据截至日期格式获取倒计时&&时间戳转日期格式
//时间戳转日期格式,传入时间戳必须为数字类型function currentDate(shijianchuo) { var date = new Date(shijianchuo); var y = ...
- 数据结构之线性顺序表ArrayList(Java实现)
一.ListMe接口: import java.util.ArrayList; //实现线性表(顺序表和链表)的接口://提供add get isEmpty size 功能public interfa ...
- spark学习(2)---RDD
一.打印RDD内容 https://blog.csdn.net/wengyupeng/article/details/52808503 1.方法 2种方式: 1 rdd.collect().forea ...
- jquery对JSON字符串的解析--eval函数
jquery eval解析JSON中的注意点介绍----https://www.jb51.net/article/40842.htm
- HDU 1525 Euclid Game
题目大意: 给定2个数a , b,假定b>=a总是从b中取走一个a的整数倍,也就是让 b-k*a(k*a<=b) 每人执行一步这个操作,最后得到0的人胜利结束游戏 (0,a)是一个终止态P ...
- codeforces 361A
//这题看着吓人,为何这么水 #include<stdio.h> int main() { int n,m,i,j; while(scanf("%d%d",& ...