muduo的并发模型为one loop per thread+ threadpool。为了方便使用,muduo封装了EventLoop和Thread为EventLoopThread,为了方便使用线程池,又把EventLoopThread封装为EventLoopThreadPool。

所以这篇博文并没有涉及到新奇的技术。可是也有一些封装和逻辑方面的注意点须要我们去分析和理解。

EventLoopThread

不论什么一个线程,仅仅要创建并执行了EventLoop,就是一个IO线程。 EventLoopThread类就是一个封装好了的IO线程。

EventLoopThread的工作流程为: 

1、在主线程创建EventLoopThread对象。

2、主线程调用EventLoopThread.start(),启动EventLoopThread中的线程(称为IO线程),而且主线程要等待IO线程创建完毕EventLoop对象。 

3、IO线程调用threadFunc创建EventLoop对象。通知主线程已经创建完毕。 

4、主线程返回创建的EventLoop对象。

EventLoopThread.h

class EventLoopThread : boost::noncopyable
{
public:
typedef boost::function<void(EventLoop*)> ThreadInitCallback; EventLoopThread(const ThreadInitCallback& cb = ThreadInitCallback());
~EventLoopThread();
EventLoop* startLoop(); // 启动线程,该线程就成为了IO线程 private:
void threadFunc(); // 线程函数 EventLoop* loop_; // loop_指针指向一个EventLoop对象
bool exiting_;
Thread thread_;
MutexLock mutex_;
Condition cond_;
ThreadInitCallback callback_; // 回调函数在EventLoop::loop事件循环之前被调用
};

EventLoopThread.cc

EventLoopThread::EventLoopThread(const ThreadInitCallback& cb)
: loop_(NULL),
exiting_(false),
thread_(boost::bind(&EventLoopThread::threadFunc, this)),
mutex_(),
cond_(mutex_),
callback_(cb)
{
} EventLoopThread::~EventLoopThread()
{
exiting_ = true;
loop_->quit(); // 退出IO线程,让IO线程的loop循环退出。从而退出了IO线程
thread_.join(); //等待线程退出
} EventLoop* EventLoopThread::startLoop()
{
assert(!thread_.started());
thread_.start();//线程启动,调用threadFunc() {
MutexLockGuard lock(mutex_);
while (loop_ == NULL)
{
cond_.wait();//须要等待EventLoop对象的创建
}
} return loop_;
} void EventLoopThread::threadFunc()
{
EventLoop loop; if (callback_)
{
callback_(&loop);
} {
MutexLockGuard lock(mutex_);
// loop_指针指向了一个栈上的对象,threadFunc函数退出之后。这个指针就失效了
// threadFunc函数退出,就意味着线程退出了,EventLoopThread对象也就没有存在的价值了。
// 因而不会有什么大的问题
loop_ = &loop;
cond_.notify(); //创建好,发送通知
} loop.loop();// 会在这里循环,直到EventLoopThread析构。此后不再使用loop_訪问EventLoop了
//assert(exiting_);
}

測试程序:

#include <muduo/net/EventLoop.h>
#include <muduo/net/EventLoopThread.h> #include <stdio.h> using namespace muduo;
using namespace muduo::net; void runInThread()
{
printf("runInThread(): pid = %d, tid = %d\n",
getpid(), CurrentThread::tid());
} int main()
{
printf("main(): pid = %d, tid = %d\n",
getpid(), CurrentThread::tid()); EventLoopThread loopThread;
EventLoop* loop = loopThread.startLoop();
// 异步调用runInThread,即将runInThread加入到loop对象所在IO线程,让该IO线程运行
loop->runInLoop(runInThread);
sleep(1);
// runAfter内部也调用了runInLoop。所以这里也是异步调用
loop->runAfter(2, runInThread);
sleep(3);
loop->quit(); printf("exit main().\n");
}

对调用过程进行分析:(查看日志)

主线程调用 loop->runInLoop(runInThread);
因为主线程(不是IO线程)调用runInLoop。 故调用queueInLoop()
将runInThead
加入到队列,然后wakeup() IO线程。IO线程在doPendingFunctors()
中取loop->runAfter()
要唤醒一下,此时仅仅是运行runAfter()
加入了一个2s的定时器, 2s超时。timerfd_
可读,先handleRead()一下然后运行回调函数runInThread()。

那为什么exit main()
之后wakeupFd_
还会有可读事件呢?那是由于EventLoopThead
栈上对象析构,在析构函数内 loop_ ->quit(),
因为不是在IO线程调用quit(),故也须要唤醒一下。IO线程才干从poll
返回,这样再次循环推断 while (!quit_)
就能退出IO线程。

EventLoopThreadPool

muduo的线程模型:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

muduo的思想时eventLoop+thread pool。为了更方便使用,将EventLoopThread做了封装。main reactor能够创建sub reactor,并发一些任务分发到sub reactor中去。EventLoopThreadPool的思想比較简单,用一个main reactor创建EventLoopThreadPool。在EventLoopThreadPool中将EventLoop和Thread绑定,能够返回EventLoop对象来使用EventLoopThreadPool中的Thread。

EventLoopThreadPool.h

class EventLoopThreadPool : boost::noncopyable
{
public:
typedef boost::function<void(EventLoop*)> ThreadInitCallback; EventLoopThreadPool(EventLoop* baseLoop);
~EventLoopThreadPool();
void setThreadNum(int numThreads) { numThreads_ = numThreads; }
void start(const ThreadInitCallback& cb = ThreadInitCallback());
EventLoop* getNextLoop(); private: EventLoop* baseLoop_; // 与Acceptor所属EventLoop同样
bool started_;
int numThreads_; // 线程数
int next_; // 新连接到来。所选择的EventLoop对象下标
boost::ptr_vector<EventLoopThread> threads_; // IO线程列表
std::vector<EventLoop*> loops_; // EventLoop列表
};

EventLoopThreadPool.cc

EventLoopThreadPool::EventLoopThreadPool(EventLoop* baseLoop)
: baseLoop_(baseLoop),
started_(false),
numThreads_(0),
next_(0)
{
} EventLoopThreadPool::~EventLoopThreadPool()
{
// Don't delete loop, it's stack variable
} void EventLoopThreadPool::start(const ThreadInitCallback& cb)
{
assert(!started_);
baseLoop_->assertInLoopThread(); started_ = true; for (int i = 0; i < numThreads_; ++i)
{
EventLoopThread* t = new EventLoopThread(cb);
threads_.push_back(t);
loops_.push_back(t->startLoop()); // 启动EventLoopThread线程。在进入事件循环之前。会调用cb
}
if (numThreads_ == 0 && cb)
{
// 仅仅有一个EventLoop。在这个EventLoop进入事件循环之前,调用cb
cb(baseLoop_);
}
} EventLoop* EventLoopThreadPool::getNextLoop()
{
baseLoop_->assertInLoopThread();
EventLoop* loop = baseLoop_; // 假设loops_为空,则loop指向baseLoop_
// 假设不为空,依照round-robin(RR。轮叫)的调度方式选择一个EventLoop
if (!loops_.empty())
{
// round-robin
loop = loops_[next_];
++next_;
if (implicit_cast<size_t>(next_) >= loops_.size())
{
next_ = 0;
}
}
return loop;
}

mainReactor关注监听事件,已连接套接字事件轮询给线程池中的subReactors 处理,一个新的连接相应一个subReactor

我们採用round-robin(RR,轮叫)的调度方式选择一个EventLoop,也就是getNextLoop函数。极端情况下,线程池中个数为0时,那么新的连接交给mainReactor。这样就退化成单线程的模式。

Muduo网络库源代码分析(四)EventLoopThread和EventLoopThreadPool的封装的更多相关文章

  1. Muduo网络库源代码分析(六)TcpConnection 的生存期管理

    TcpConnection是使用shared_ptr来管理的类,由于它的生命周期模糊.TcpConnection表示已经建立或正在建立的连接.建立连接后,用户仅仅须要在上层类如TcpServer中设置 ...

  2. muduo网络库学习笔记(四) 通过eventfd实现的事件通知机制

    目录 muduo网络库学习笔记(四) 通过eventfd实现的事件通知机制 eventfd的使用 eventfd系统函数 使用示例 EventLoop对eventfd的封装 工作时序 runInLoo ...

  3. muduo网络库架构总结

    目录 muduo网络库简介 muduo网络库模块组成 Recator反应器 EventLoop的两个组件 TimerQueue定时器 Eventfd Connector和Acceptor连接器和监听器 ...

  4. 长文梳理muduo网络库核心代码、剖析优秀编程细节

    前言 muduo库是陈硕个人开发的tcp网络编程库,支持Reactor模型,推荐大家阅读陈硕写的<Linux多线程服务端编程:使用muduo C++网络库>.本人前段时间出于个人学习.找工 ...

  5. muduo 网络库学习之路(一)

    前提介绍: 本人是一名大三学生,主要使用C++开发,兴趣是高性能的服务器方面. 网络开发离不开网络库,所以今天开始学一个新的网络库,陈老师的muduo库 我参考的书籍就是陈老师自己关于muduo而编著 ...

  6. 陈硕 - Linux 多线程服务端编程 - muduo 网络库作者

    http://chenshuo.com/book/ Muduo网络库源码分析(一) EventLoop事件循环(Poller和Channel)http://blog.csdn.net/nk_test/ ...

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

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

  8. muduo网络库使用心得

    上个月看了朋友推荐的mudo网络库,下完代码得知是国内同行的开源作品,甚是敬佩.下了mudo使用手冊和035版的代码看了下结构,感觉是一个比較成熟并且方便使用的网络库.本人手头也有自己的网络库,尽管不 ...

  9. muduo网络库学习笔记(五) 链接器Connector与监听器Acceptor

    目录 muduo网络库学习笔记(五) 链接器Connector与监听器Acceptor Connector 系统函数connect 处理非阻塞connect的步骤: Connetor时序图 Accep ...

随机推荐

  1. IO流(一)File类

    1.File类:表示文件和目录路径的抽象的表示形式,可以实现文件的创建,删除,重命名等,是唯一与文件本 有关的操作类. 2.File类的API定义:public class File extends ...

  2. mongoDB - 插入数据

    db.use.js /** * 使用前:先安装node环境.express.mongodb. * * 1.1 安装Node * 1.2 npm i -D express mongodb &&a ...

  3. 一扫天下——ZXing使用全解析

    一扫天下--ZXing使用全解析 二维码如今已经烂App了,无论什么App.没有二维码就好像低人一等了. 所以,在自己的项目中集成二维码功能还是非常有必要的. 网上非常多都是基于ZXing2.3的.可 ...

  4. Python_Select解析

    selcet(等待I/O完成)的介绍: select同时监控多个socket,select()的机制提供了fd_set的数据结构,实际是long类型的数组,优点是跨平台性,select的缺点在于单个进 ...

  5. Nmon命令行:Linux系统性能的监测利器

    如果你眼下正在寻找一款非常易于使用的Linux性能监测工具,那么我强烈推荐安装和使用Nmon命令行实用工具. Nmon监测工具 Nmon是一款面向系统管理员的调优和基准测量工具,可以用来显示关于下列方 ...

  6. react native使用 mobx , can't find variable:Symbol

    原因是因为 mobx的版本用的最新版本..用到了 Symbol部分es6的api特性. 解决问题办法 1. 把mobx降版本到 4.3.1 . mobx-react降版本到 5.1.0 即可. 或者 ...

  7. 会话管理之session技术

    上一节我们总结了cookie技术,这节主要总结一下session技术. 1. session对象 在web开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占 ...

  8. java调用本地默认浏览器

    1 http://blog.csdn.net/casularm/article/details/3401018 2

  9. 转 【MQTT】在Windows下搭建MQTT服务器

    MQTT简介 MQ 遥测传输 (MQTT) 是轻量级基于代理的发布/订阅的消息传输协议,设计思想是开放.简单.轻量.易于实现.这些特点使它适用于受限环境.该协议的特点有: 使用发布/订阅消息模式,提供 ...

  10. java 中的 i++ 和 ++i

    熟悉c/c++中的i++和++i,那么你知道下面的java代码效果是什么吗? 一 . 代码示例 /** * * @author elelule * */ public class TestPlusPl ...