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. deallocvt - 释放未使用的虚拟终端

    SYNOPSIS(总览) deallocvt [ N1 N2 ... ] DESCRIPTION(描述) 如果不指定参数, deallocvt 程序会释放所有未使用的虚拟终端的核心内存和数据结构. 如 ...

  2. 他山之石:D3DX书籍推荐

    一.DirectX9.0.3D游戏开发编程基础(龙书) 这本书网上评论很好,当初作为入门书看确实还不错,但是看得有点稀里糊涂的.现在回过头来看,它有些地方省略了,所以还得自己多多总结,总得来说还是值得 ...

  3. SVN文件库移植(转)

     SVN文件库移植(转) 分类: 项目管理2013-04-19 11:06 161人阅读 评论(0) 收藏 举报 公司以前用的SVN是安装在windows2003下,用了一年多,现在大家觉得很慢,强烈 ...

  4. 关于OpenMP的归约操作reduction

    这里提一个重要的点 像这样 ; void ff() { sum += 0.5; } //main() #pragma omp parallel for reduction(+:sum) ; i < ...

  5. 虚拟机Linux与本地虚拟网卡配置---NAT链接方式

    虚拟机Linux与本地虚拟网卡配置---NAT链接方式 **********这是我亲自尝试多次实践出来的结果,不是复制粘贴************************* 首先进行初始化,这样避免有 ...

  6. sed输出指定行

    and line ,8p to line ,8p -e 20p - and line -n:取消默认输出.注意:sed命令会默认把输入行打印到屏幕上,所以如果想精准的控制输出,就需要-n. -e:进行 ...

  7. node函数assert()详解

    assert.ok() 的别名. const assert = require('assert'); assert(true); // OK assert(1); // OK assert(false ...

  8. solr-5.3.1配置(win7 x64)

    下载solr,下载地址http://www.eu.apache.org/dist/lucene/solr/5.3.1/solr-5.3.1.zip 解压到某个目录下,这里是解压到了d盘目录下,路径:D ...

  9. 爬虫基础spider 之(一) --- 初识爬虫

    爬虫概念 (spider,网络蜘蛛)通过互联网上一个个的网络节点,进行数据的提取.整合以及存储.从而获取我们想要的部分 robots协议 robots协议不是技术层面的协议,只是一个君子协定: 首先在 ...

  10. LeetCode(29)Divide Two Integers

    题目 Divide two integers without using multiplication, division and mod operator. If it is overflow, r ...