模型

实现流程:

muduo网络库有一个事件驱动循环线程池EventLoopThreadPool,线程池中有多个事件驱动线程EventLoopThread,每个线程运行一个EventLoop事件循环,每个事件循环调用io多路复用的EPoller,EPoller可以监听多个Channel,每个Channel对应一个fd,在Channel被激活后调用回调函数,回调函数在EventLoop所在线程执行。

源码分析

构造函数

EventLoop::EventLoop(): looping_(false),quit_(false),eventHandling_(false),callingPendingFunctors_(false),iteration_(0),threadId_(CurrentThread::tid()),               // 记录当前线程IDpoller_(Poller::newDefaultPoller(this)),       // 创建poller对象timerQueue_(new TimerQueue(this)),             // 创建定时器队列wakeupFd_(createEventfd()),                    // 创建唤醒fdwakeupChannel_(new Channel(this, wakeupFd_)),  // 创建被唤醒的channelcurrentActiveChannel_(NULL)                    // 当前活跃的channel
{LOG_DEBUG << "EventLoop created " << this << " in thread " << threadId_;if (t_loopInThisThread)                          // 保证是在当前线程中运行{LOG_FATAL << "Another EventLoop " << t_loopInThisThread<< " exists in this thread " << threadId_;}else{t_loopInThisThread = this;}wakeupChannel_->setReadCallback(std::bind(&EventLoop::handleRead, this));// we are always reading the wakeupfdwakeupChannel_->enableReading();      // 使能读事件
}

析构函数

EventLoop::~EventLoop()
{LOG_DEBUG << "EventLoop " << this << " of thread " << threadId_<< " destructs in thread " << CurrentThread::tid();wakeupChannel_->disableAll();wakeupChannel_->remove();        // 移除所有通道::close(wakeupFd_);t_loopInThisThread = NULL;
}

runInLoop

void EventLoop::runInLoop(Functor cb)
{if (isInLoopThread())   // 如果是当前线程, 直接执行回调{cb();}else                    // 如果不是当前线程,加入到队列中等待本次循环结束后执行回调{queueInLoop(std::move(cb));}
}void EventLoop::queueInLoop(Functor cb)
{{MutexLockGuard lock(mutex_);pendingFunctors_.push_back(std::move(cb));}if (!isInLoopThread() || callingPendingFunctors_){wakeup();}
}

唤醒线程的情况:

1) 不是当前IO线程;
2) 当前正在调用pending functor;

loop

loop在线程池的单个线程中被调用:

void EventLoopThread::threadFunc()
{EventLoop loop;if (callback_){callback_(&loop);}{MutexLockGuard lock(mutex_);loop_ = &loop;cond_.notify();}loop.loop();//assert(exiting_);MutexLockGuard lock(mutex_);loop_ = NULL;
}

loop实现部分:

void EventLoop::loop()
{assert(!looping_);assertInLoopThread();looping_ = true;quit_ = false;  // FIXME: what if someone calls quit() before loop() ?LOG_TRACE << "EventLoop " << this << " start looping";while (!quit_){activeChannels_.clear();pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);  // 等待被激活的通道 即有新事件产生的通道++iteration_;if (Logger::logLevel() <= Logger::TRACE){printActiveChannels();}// TODO sort channel by priorityeventHandling_ = true;for (Channel* channel : activeChannels_)  // 遍历激活的通道 执行回调{currentActiveChannel_ = channel;currentActiveChannel_->handleEvent(pollReturnTime_);   // 根据revents_的值调用不同的用户回调}currentActiveChannel_ = NULL;eventHandling_ = false;doPendingFunctors();  // }LOG_TRACE << "EventLoop " << this << " stop looping";looping_ = false;
}

doPendingFunctors

void EventLoop::doPendingFunctors()
{std::vector<Functor> functors;callingPendingFunctors_ = true;{MutexLockGuard lock(mutex_);functors.swap(pendingFunctors_);}for (const Functor& functor : functors){functor();}callingPendingFunctors_ = false;
}

doPendingFunctors的作用:

把回调列表swap()到局部变量functors中,一方面减小了临界区的长度(意味着不会阻塞其他线程调用queueInLoop()),另一方面也避免了死锁(因为Functor可能在调用queueInLoop)。由于doPendingFunctors()调用的Functor可能再调用queueInLoop(cb),这时queueInLoop()就必须wakeup(),否则这些新家的cb就不能被及时调用了。muduo这里没有反复执行doPendingFunctors()直到pendingFunctors_为空,这里是有意的,否则IO线程有可能陷入死循环,无法处理IO事件。

updateChannel和removeChannel

调用的是poller中封装的epoll_ctl接口,将channel加入到poll中,当有激活事件时做相应回调。

void EventLoop::updateChannel(Channel* channel)
{assert(channel->ownerLoop() == this);  // 是否是当前loopassertInLoopThread();                  // 是否是当前线程poller_->updateChannel(channel);
}void EventLoop::removeChannel(Channel* channel)
{assert(channel->ownerLoop() == this);   // 是否是当前loopassertInLoopThread();                   // 是否是当前线程if (eventHandling_){assert(currentActiveChannel_ == channel ||std::find(activeChannels_.begin(), activeChannels_.end(), channel) == activeChannels_.end());}poller_->removeChannel(channel);
}

muduo网络库——EventLoop相关推荐

  1. muduo网络库学习(四)事件驱动循环EventLoop

    muduo的设计采用高并发服务器框架中的one loop per thread模式,即一个线程一个事件循环. 这里的loop,其实就是muduo中的EventLoop,所以到目前为止,不管是Polle ...

  2. muduo网络库源码复现笔记(十七):什么都不做的EventLoop

    Muduo网络库简介 muduo 是一个基于 Reactor 模式的现代 C++ 网络库,作者陈硕.它采用非阻塞 IO 模型,基于事件驱动和回调,原生支持多核多线程,适合编写 Linux 服务端多线程 ...

  3. muduo网络库学习(八)事件驱动循环线程池EventLoopThreadPool

    muduo是支持多线程的网络库,在muduo网络库学习(七)用于创建服务器的类TcpServer中也提及了TcpServer中有一个事件驱动循环线程池,线程池中存在大量线程,每个线程运行一个Event ...

  4. muduo网络库学习(七)用于创建服务器的类TcpServer

    目前为止,涉及到的绝大多数操作都没有提及线程,EventLoop,Poller,Channel,Acceptor,TcpConnection,这些对象的执行都是在单独线程完成,并没有设计多线程的创建销 ...

  5. 基于C++11的muduo网络库

    文章目录 写在前面 项目编译问题 库安装的问题 项目测试代码 关于压力测试 项目概述 muduo网络库的reactor模型 muduo的设计 muduo各个类 辅助类 NonCopyable Time ...

  6. muduo网络库的封装

    一.基础socket编程 网络编程的底层离不开socket,其处理流程表示如下: int sockfd = socket(AF_INET, SOCK_STREAM, 0);struct sockadd ...

  7. muduo网络库浅谈(一)

    muduo网络库浅谈(一) 序言 第一章 muduo的关键结构 class EventLoop class Channel class Poller 番外 定时任务 class Timestamp c ...

  8. Muduo网络库核心梳理

    Muduo网络库 Muduo网络库本身并不复杂,是一个新手入门C++面向对象网络编程的经典实战项目.但是,新手在刚刚上手读代码的时候,非常容易陷入代码的汪洋大海,迷失方向.本文旨在简要梳理Muduo网 ...

  9. C++练手项目(基于muduo网络库+mysql+jsoncpp的简易HTTPWebServer用于网页显示数据库后台数据

    基于muduo网络库+mysql+jsoncpp的简易HTTPWebServer 项目介绍 背景介绍 主要模块介绍 1.基于muduo网络库的WebServer: 2.HTTP协议栈 3.JsonSe ...

最新文章

  1. Unable to locate package错误解决办法
  2. php+js实现异步图片上传,JavaScript实现异步图像上传功能
  3. thinkphp3.2.3 找不到自定义模型_Orion HTC VIVE高性价比动作捕捉,虚拟直播 支持UE4.25 导入自定义模型...
  4. cf1107e uva10559区间dp升维
  5. html画图代码_python之matplotlib画图教程(1)
  6. php vm_facebook hiphop php vm 兑现概述(二)
  7. 使用fastcgi_cache加速你的Nginx网站
  8. Android ndk下载和环境配置
  9. nmds与mds的区别_聚类分析和NMDS分析的基本步骤.doc
  10. 深度学习-训练集图片输入神经网络前的标准化(附代码)
  11. 小米笔记本ubantu20.04安装输入法和Nvidia驱动 实录
  12. 用python成为顶级黑客-python绝技:运用python成为顶级黑客 PDF 超清中文版
  13. UG NX二次开发(C#)-建模-获取曲面的法矢
  14. 7-22 循环日程表
  15. Git追加本次提交到上次提交
  16. 如何查询网站IP地址
  17. Verilog 与门
  18. オフショア開発を成功させる工夫10点
  19. stm32学习笔记-翻译官ADC
  20. 一个c语言程序的开发环境,C语言入门(2)——安装VS2013开发环境并编写第一个C语言程序...

热门文章

  1. 概率机器人——多元正态分布密度函数
  2. 软件测试(用例Ⅰ)· 测试用例的基本概念 · 万能公式 · 设计测试用例的方法(等价类、边界值、判定表法、正交法、场景设计法、错误猜测法)
  3. 【JAVA】超简单的1-100猜数字小游戏
  4. GOOGLE 云端硬盘
  5. 钉钉小程序换行和输入空格
  6. linux网卡ens33,Linux网卡没有eth0显示ens33原因以及解决办法
  7. Jmeter获取当前时间、历史时间、未来时间的方式
  8. php socket之websocket
  9. 机器人相关专业本科学业的重要性
  10. Wordpress最简单完整网站搬家教程,以本地为例