一句话讲懂:muduo网络库——Poller

Tomcat 三大核心线程组件Acceptor、Poller 和 Executor&为什么tomcat 要把Acceptor单独抽出来,不和read、write一起放到事件循环里? 接受连接:Acceptor 线程监听端口,接受新的客户端连接。 注册事件:接受到连接后,Acceptor 将连接注册到 Poller 中,等待事件发生。 事件监控:Poller 线程监控连接上的事件,如数据可读或可写。 事件处理:当事件发生时,Poller 将事件分发给处理器(如请求处理器)进行处理。 请求处理:处理器从 Executor 的线程池中获取线程来处理请求。 阅读详情

OK了兄弟们。
今天咱们分析一手,muduo 网络库的——Poller 类

我们的 Poller 类其实是搞抽象的啊,一个抽象类(没实例呀,牢弟)。这个类主要是为了派生 EpollPoller 类和 PollPoller 类。这两个派生类 也很顾名思义,分别是为了封装 epoll 和 poll 两个模型。

Poller.h 文件:

class Poller : noncopyable
{
 public:
  typedef std::vector<Channel*> ChannelList;

  Poller(EventLoop* loop);
  virtual ~Poller();

  virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels) = 0; // timeoutMs 是超时时间,activeChannels 是一个传出参数

  virtual void updateChannel(Channel* channel) = 0; //向模型中加入新的文件描述符,或更新文件描述符关心的事件

  virtual void removeChannel(Channel* channel) = 0; //从模型中移除文件描述符

  virtual bool hasChannel(Channel* channel) const; //看看是不是已经有这个channel

  static Poller* newDefaultPoller(EventLoop* loop); //给 EventLoop 构造属于他的 poller 用

  void assertInLoopThread() const
  {
    ownerLoop_->assertInLoopThread();
  }

 protected:
  typedef std::map<int, Channel*> ChannelMap;
  ChannelMap channels_; //文件描述符和对应的 channel

 private:
  EventLoop* ownerLoop_; //指向所属的 EventLoop
};

EpollPoller 类:

相比于基类,多了两个变量:

  typedef std::vector<struct epoll_event> EventList;

  int epollfd_;
  EventList events_;

新增了一个函数用于找出有事件发生的文件描述符:

void EPollPoller::fillActiveChannels(int numEvents, ChannelList* activeChannels) const
{
  assert(implicit_cast<size_t>(numEvents) <= events_.size());
  for (int i = 0; i < numEvents; ++i)
  {
    Channel* channel = static_cast<Channel*>(events_[i].data.ptr);
#ifndef NDEBUG
    int fd = channel->fd();
    ChannelMap::const_iterator it = channels_.find(fd);
    assert(it != channels_.end());
    assert(it->second == channel);
#endif
    channel->set_revents(events_[i].events);
    activeChannels->push_back(channel);
  }
}

一句注释也没写。
没啥可写的。

还有一个函数,用于调用 epoll_ctl 函数:

void EPollPoller::update(int operation, Channel* channel)
{
  struct epoll_event event;
  memZero(&event, sizeof event);
  event.events = channel->events();
  event.data.ptr = channel;
  int fd = channel->fd();
  LOG_TRACE << "epoll_ctl op = " << operationToString(operation)
    << " fd = " << fd << " event = { " << channel->eventsToString() << " }";
  if (::epoll_ctl(epollfd_, operation, fd, &event) < 0)
  {
    if (operation == EPOLL_CTL_DEL)
    {
      LOG_SYSERR << "epoll_ctl op =" << operationToString(operation) << " fd =" << fd;
    }
    else
    {
      LOG_SYSFATAL << "epoll_ctl op =" << operationToString(operation) << " fd =" << fd;
    }
  }
}

其实还有一个函数,输出用的:

const char* EPollPoller::operationToString(int op)
{
  switch (op)
  {
    case EPOLL_CTL_ADD:
      return "ADD";
    case EPOLL_CTL_DEL:
      return "DEL";
    case EPOLL_CTL_MOD:
      return "MOD";
    default:
      assert(false && "ERROR op");
      return "Unknown Operation";
  }
}

这回真没有新增的函数了,剩下的都是重写基类的函数了。也是比较重要的部分。

看看 EPollPoller::poll 函数吧:

Timestamp EPollPoller::poll(int timeoutMs, ChannelList* activeChannels)
{
  LOG_TRACE << "fd total count " << channels_.size();
  int numEvents = ::epoll_wait(epollfd_,
                               &*events_.begin(),
                               static_cast<int>(events_.size()),
                               timeoutMs);
  int savedErrno = errno;
  Timestamp now(Timestamp::now());
  if (numEvents > 0) // 有事发生
  {
    LOG_TRACE << numEvents << " events happened";
    fillActiveChannels(numEvents, activeChannels);
    if (implicit_cast<size_t>(numEvents) == events_.size())
    {
      events_.resize(events_.size()*2);// 扩容
    }
  }
  else if (numEvents == 0) // 无事发生
  {
    LOG_TRACE << "nothing happened";
  }
  else // 有错发生
  {
    // error happens, log uncommon ones
    if (savedErrno != EINTR)
    {
      errno = savedErrno;
      LOG_SYSERR << "EPollPoller::poll()";
    }
  }
  return now;
}

EPollPoller::updateChannel 函数:

void EPollPoller::updateChannel(Channel* channel)
{
  Poller::assertInLoopThread();
  const int index = channel->index();
  LOG_TRACE << "fd = " << channel->fd()
    << " events = " << channel->events() << " index = " << index;
  if (index == kNew || index == kDeleted) // 新来的或者曾经注册过的,需要再次注册到 epoll 中
  {
    int fd = channel->fd();
    if (index == kNew)
    {
      assert(channels_.find(fd) == channels_.end());
      channels_[fd] = channel; // 登记到表中
    }
    else // index == kDeleted
    {
      assert(channels_.find(fd) != channels_.end());
      assert(channels_[fd] == channel);
    }

    channel->set_index(kAdded); // 更新状态
    update(EPOLL_CTL_ADD, channel); // 加入 epoll
  }
  else // 已经在 epoll 中
  {
    int fd = channel->fd();
    (void)fd;
    assert(channels_.find(fd) != channels_.end());
    assert(channels_[fd] == channel);
    assert(index == kAdded);
    if (channel->isNoneEvent()) // 没什么关注的事
    {
      update(EPOLL_CTL_DEL, channel); // 删掉
      channel->set_index(kDeleted);
    }
    else // 有所留恋
    {
      update(EPOLL_CTL_MOD, channel); 
    }
  }
}

EPollPoller::removeChannel 函数:

void EPollPoller::removeChannel(Channel* channel)
{
  Poller::assertInLoopThread();
  int fd = channel->fd();
  LOG_TRACE << "fd = " << fd;
  assert(channels_.find(fd) != channels_.end());
  assert(channels_[fd] == channel);
  assert(channel->isNoneEvent());
  int index = channel->index();
  assert(index == kAdded || index == kDeleted);
  size_t n = channels_.erase(fd); // 从表里面开除
  (void)n;
  assert(n == 1);

  if (index == kAdded) //如果注册在 epoll 中
  {
    update(EPOLL_CTL_DEL, channel); //从 epoll 中开除
  }
  channel->set_index(kNew); //更新状态
}

PollPoller 类:

不想讲。
把代码贴上吧,和 epoll 大差不差的

class PollPoller : public Poller
{
 public:

  PollPoller(EventLoop* loop);
  ~PollPoller() override;

  Timestamp poll(int timeoutMs, ChannelList* activeChannels) override;
  void updateChannel(Channel* channel) override;
  void removeChannel(Channel* channel) override;

 private:
  void fillActiveChannels(int numEvents,
                          ChannelList* activeChannels) const;

  typedef std::vector<struct pollfd> PollFdList;
  PollFdList pollfds_;
};
PollPoller::PollPoller(EventLoop* loop)
  : Poller(loop)
{
}

PollPoller::~PollPoller() = default;

Timestamp PollPoller::poll(int timeoutMs, ChannelList* activeChannels)
{
  // XXX pollfds_ shouldn't change
  int numEvents = ::poll(&*pollfds_.begin(), pollfds_.size(), timeoutMs);
  int savedErrno = errno;
  Timestamp now(Timestamp::now());
  if (numEvents > 0)
  {
    LOG_TRACE << numEvents << " events happened";
    fillActiveChannels(numEvents, activeChannels);
  }
  else if (numEvents == 0)
  {
    LOG_TRACE << " nothing happened";
  }
  else
  {
    if (savedErrno != EINTR)
    {
      errno = savedErrno;
      LOG_SYSERR << "PollPoller::poll()";
    }
  }
  return now;
}

void PollPoller::fillActiveChannels(int numEvents,
                                    ChannelList* activeChannels) const
{
  for (PollFdList::const_iterator pfd = pollfds_.begin();
      pfd != pollfds_.end() && numEvents > 0; ++pfd)
  {
    if (pfd->revents > 0)
    {
      --numEvents;
      ChannelMap::const_iterator ch = channels_.find(pfd->fd);
      assert(ch != channels_.end());
      Channel* channel = ch->second;
      assert(channel->fd() == pfd->fd);
      channel->set_revents(pfd->revents);
      // pfd->revents = 0;
      activeChannels->push_back(channel);
    }
  }
}

void PollPoller::updateChannel(Channel* channel)
{
  Poller::assertInLoopThread();
  LOG_TRACE << "fd = " << channel->fd() << " events = " << channel->events();
  if (channel->index() < 0)
  {
    // a new one, add to pollfds_
    assert(channels_.find(channel->fd()) == channels_.end());
    struct pollfd pfd;
    pfd.fd = channel->fd();
    pfd.events = static_cast<short>(channel->events());
    pfd.revents = 0;
    pollfds_.push_back(pfd);
    int idx = static_cast<int>(pollfds_.size())-1;
    channel->set_index(idx);
    channels_[pfd.fd] = channel;
  }
  else
  {
    // update existing one
    assert(channels_.find(channel->fd()) != channels_.end());
    assert(channels_[channel->fd()] == channel);
    int idx = channel->index();
    assert(0 <= idx && idx < static_cast<int>(pollfds_.size()));
    struct pollfd& pfd = pollfds_[idx];
    assert(pfd.fd == channel->fd() || pfd.fd == -channel->fd()-1);
    pfd.fd = channel->fd();
    pfd.events = static_cast<short>(channel->events());
    pfd.revents = 0;
    if (channel->isNoneEvent())
    {
      // ignore this pollfd
      pfd.fd = -channel->fd()-1;
    }
  }
}

void PollPoller::removeChannel(Channel* channel)
{
  Poller::assertInLoopThread();
  LOG_TRACE << "fd = " << channel->fd();
  assert(channels_.find(channel->fd()) != channels_.end());
  assert(channels_[channel->fd()] == channel);
  assert(channel->isNoneEvent());
  int idx = channel->index();
  assert(0 <= idx && idx < static_cast<int>(pollfds_.size()));
  const struct pollfd& pfd = pollfds_[idx]; (void)pfd;
  assert(pfd.fd == -channel->fd()-1 && pfd.events == channel->events());
  size_t n = channels_.erase(channel->fd());
  assert(n == 1); (void)n;
  if (implicit_cast<size_t>(idx) == pollfds_.size()-1)
  {
    pollfds_.pop_back();
  }
  else
  {
    int channelAtEnd = pollfds_.back().fd;
    iter_swap(pollfds_.begin()+idx, pollfds_.end()-1);
    if (channelAtEnd < 0)
    {
      channelAtEnd = -channelAtEnd-1;
    }
    channels_[channelAtEnd]->set_index(idx);
    pollfds_.pop_back();
  }
}
muduo网络库学习总结:基本架构及流程分析 muduo网络库学习:基本架构及流程分析基本架构Basic ReactorMutiple Reactor + ThreadPoolmuduo库的基本使用基本结构介绍EventLoop类Poller类Channel类TcpConnection类Acceptor类TcpServer类流程梳理 之前一直在学习陈硕大佬的muduo库,一直考虑找个时间总结一下(不然没过多久全忘光了)。muduo库是一种基于Reactor模型的C++网络库,仅支持Linux平台,相关学习可以参考《Linux 多线程服务器端编程》。 阅读详情

相关推荐

[Muduo网络库]:三大核心库Channel类、Poller/EpollPoller类以及EventLoop类

除了循环,什么都没做的loop()",这句话的意思并非是loop()只是一个空循环,而是它的主要功能并未通过自身定义的函数实现。loop函数中主要通过调用一次Poller::poll方法它就能给你返回事件监听器的监听结果,然后调用这些Channel里面保管的不同类型事件的处理函数。loop()函数最主要的作用是实现循环,负责驱动“循环”的重要模块。Channel类其实相当于一个文件描述符的保姆!2.2.1 Channel类概述-我在地铁站里吃闸机。

weixin_45807759的博客 1428

Poller模块 --- 事件监控模块

Poller模块,对epoll的操作进行封装以及管理

weixin_64099089的博客 596

【C++实现】从0简单理解muduo网络库

陈硕大神的开源网络库muduo库,本文进行剖析~!

jhao的博客 3195

Tomcat NIO(8)-Poller线程的阻塞与唤醒

在上一篇文章里我们主要介绍了 tomcat NIO 中的poller线程,包括启动 poller 线程,添加事件到事件队列,对原始 socket 注册事件和 poller 线程的核...

weixin_46073333的博客 866

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

muduo的设计采用高并发服务器框架中的one loop per thread模式,即一个线程一个事件循环。 这里的loop,其实就是muduo中的EventLoop,所以到目前为止,不管是Poller,Channel还是TimerQueue都仅仅是单线程下的任务,因为这些都依赖于EventLoop。这每一个EventLoop,其实也就是一个Reactor模型。 而多线程体现在EventLoop

一个程序渣渣的小后院 3707

用C++11仿写muduo网络库

先从底层讲再学习陈硕大佬的muduo库中,真的学到了很多的新知识和思想,还有一些代码习惯也是对我受益良多,我利用了c++11的一些新特性来代替原来muduo库中对c++ boost库的依赖。但其实muduo库中还有很多的模块我还没有实现。但没办法为了今年大三上的时候要去找实习,现在必须要花更多的时间在八股文和算法上,所以后面有时间的话我还会继续更新这个项目的,毕竟还有很多的功能没有实现。所以继续努力吧!

m0_74932004的博客 1303

net3. Reactor关键结构——Poller轮询器类

Poller是一个抽象类,Poller是对I/O复用的抽象,有两个派生类PollPoller和EPollPoller,一个EventLoop包含一个Poller对象 Poller类 数据成员: EventLoop* ownerLoop_:Poller所属EventLoop typedef typedef std::vector<Channel*> ChannelList 成员函数: Poller(EventLoop* loop):构造函数,记录Poller所属EventLoop virtual

TABE_的博客 465

复用浏览器之跳过扫码登录

复用浏览器 1、通过命令启动浏览器:chrome --remote-debugging-port=9222(前提是退出所有的chrome浏览器) 2、启动浏览器之后,在地址栏输入127.0.01:9222,成功复用如下图所示:

qq_43911915的博客 2034

Poller

Poller API 是对 epoll I/O 多路复用机制的封装,核心功能包括监控文件描述符事件、更新和移除 Channel。通过 kNew/kAdded/kDeleted 三种状态管理 Channel 生命周期,确保正确的 epoll_ctl 调用。调用流程包括初始化、事件循环中的 poll 操作以及 Channel 的增删改查。状态机设计避免了重复操作和非法调用,提高了系统健壮性。整个机制为事件驱动模型提供了高效可靠的底层支持。

qq_47987543的博客 593

两级运算放大器设计与仿真

运算放大器(简称运放)是许多模拟系统和混合信号系统中的一个完整部分。各种不同复杂程度的运放被用来实现各种功能:从直流偏置的产生到高速放大或滤波。伴随者每一代 CMOS 工艺,由于电源电压和晶体管沟道长度的减小,为运放的设计不断提出复杂的课题。 运算放大器的设计可以分为两个较为独立的步骤,第一步是选择或搭建运放的基本结构,绘出电路结构草图,第二步就要选择直流电流,手工设计管子尺寸,以及设计补偿电路等等,然后在手工计算的基础上,运用模拟电路仿真软件对设计的两级运放进行仿真,并对电路进行后续的调试和修改。....

qq_50968231的博客 11万+

Cursor使用教程,有点难

Cursor Code Editor 不仅仅是一个代码编辑器,它更是一个集成了 AI 的全功能开发环境,帮助你从代码编写到优化的整个过程变得更加智能和高效。它通过内置的 AI 代码补全、错误检测和自动修复功能,大大减少了手动编写代码的负担,帮助你快速完成开发任务。同时,Cursor 还与多种前沿的 AI 大模型集成,包括 OpenAI 的 GPT-4o、Claude 3.5 和 Meta 的 Llama 3.1。

xiaofeitunAI的博客 1万+

[muduo网络库]——muduo库的Reactor模型(剖析muduo网络库核心部分、设计思想)

muduo 是由陈硕大佬个人开发的 TCP 网络库,本专栏使用c++11重构了Muduo库中核心的Multi-Reactor架构,剖析大佬核心的代码设计思想,希望能够理清自己的思路,也希望能够帮助到大家。

m0_73537205的博客 2032

【SerDes】美信GMSL2-CSI调试心得

美信GMSL2加解串器芯片家族广泛应用于车载和工业视觉系统。文章介绍了MAX96717/MAX96717F加串器和MAX96724/MAX96724R解串器的关键特性,包括支持MIPI CSI-2接口、6Gbps传输速率等功能。提供了基于高通平台的demo系统配置和调试步骤,重点阐述了Link Lock、PCLK检测和Video Lock等关键调试环节,并详细说明了像素模式,隧道模式的工作原理及寄存器配置示例。文中还包含通过I2C读写寄存器的具体操作指令,取流freeze可能原因和解决方法。

qq_38089448的博客 2494

[muduo网络库]——muduo库三大核心组件之 Poller/EpollPoller类(剖析muduo网络库核心部分、设计思想)

muduo 是由陈硕大佬个人开发的 TCP 网络库,本专栏使用c++11重构了Muduo库中核心的Multi-Reactor架构,剖析大佬核心的代码设计思想,希望能够理清自己的思路,也希望能够帮助到大家。

m0_73537205的博客 1998

ksz9031 mmd读取不了_ZYNQ. LwIP.PHY.KSZ9031RNX

新一块板子米尔科技的z-turn 使用的PHY芯片是Micrel的 KSZ9031RNX 而不是zedboard上的Marvell的。直接使用lwip的echo server demo时会报错 , 无法启动。在网上找了很久终于找到几篇关于这个问题的文章。修改PHY的驱动xemacpsif_physpeed.c文件该芯片的PHY Identifier 是 0x0022bsp设置中修改参数以提速MEM...

weixin_39626298的博客 924

OpenClaw赚钱实录:从“养龙虾“到可持续变现的实践指南

OpenClaw已经开源3个月有余,作为深耕AI Agent领域的博主,我亲身探索了各种基于OpenClaw的赚钱方法,也踩过坑、付出了时间与精力。现在,我将这些真金白银砸出来的实战经验,毫无保留地总结成这个专栏,分享给不想从零开始摸索的你。博主在优快云已有7万+粉丝和1100万+阅读量,但这却是我的第一个付费专栏。正因如此,我比任何人都更珍惜这个“第一次”。我会像一个手艺人打磨作品一样,死磕每一篇文章的质量,并持续根据订阅者的反馈优化内容。也许你的疑问就是当篇文章的优化点。

冯·诺依曼 2442

基于C++11的muduo网络库

文章目录 作者:shenmingik 邮箱:2107810343@qq.com 时间:2021/1/26 22:17 开发环境:Ubuntu VS Code 编译器:g++ 编程语言:C++ 源码连接:https://share.weiyun.com/PTtAqitJ

0 error(s) 2581

零基础学GPU KMD--8. 内存管理:GPU 显存与系统内存的协同​8.1 显存分配策略:GEM / TTM 原理

支持 VRAM、GTT(Graphics Translation Table)、SYSTEM 内存的动态迁移与缓存管理。:若缓冲区在 SYSTEM,TTM 分配 GTT 条目并建立页表映射,使 GPU 可直接访问。:系统内存中预留给 GPU 直接访问的区域,通过 GPU 的 MMU 做地址翻译。你需要我继续展开这一部分吗?​ 为复杂离散 GPU 设计,应对大容量 VRAM、多内存域、显存迁移等问题。:重量级、功能全,适合大容量、多内存域的离散 GPU,强调显存迁移与统一管理。

amy_mhd的博客 185

[muduo网络库]-muduo库Poller/EPollpoller解析

本贴用于记录库的学习过程,以下是关于Poller\EPollpoller类的个人理解。Poller\EPollpoller作为三大核心组件之一,主要功能是为eventloop监听事件,并返回待处理的channel。下面是Poller在eventloop中的大概模型:大家都知道Poller是通过子类EPollpoller实现的,但是为什么要怎么设计呢?直接把具体实现在Poller里写出来不就好了吗。

2301_81131022的博客 2131

基于Simulink的基于MPC的实时ZMP约束步态优化仿真

本文完成了基于Simulink的MPC实时ZMP约束步态优化仿真,实现了:✅ 建立3D-LIPM预测模型✅ 设计带ZMP可行域约束的MPC优化器✅ 实现闭环实时求解与扰动抑制✅ 验证强鲁棒性与安全性✅ 为人形机器人野外行走、人机交互提供先进控制方案。

xiaoheshang_123的博客 86

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

【Muduo】三大核心之Poller、EPollPoller

EPollPoller根据epoll_wait返回的文件描述符集合和事件类型,通过在epoll_event的data.ptr字段预先设置Channel对象的地址,从而能够找到对应的Channel对象,并调用其上的回调函数来处理事件。可以添加新的文件描述符、修改已注册的文件描述符的事件类型或删除已注册的文件描述符。:EPollPoller利用了epoll机制的高效性,能够同时监听大量的文件描述符,并且在有事件发生时只返回有事件发生的文件描述符集合,避免了轮询所有文件描述符的开销。

qq_20756957的博客 2394

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

muduo网络库之Poller类

本片博客针对Poller类及其2个派生类PollPoller、EPollPoller做下小结。 博客代码来自于陈硕的muduo网络库,github地址https://github.com/chenshuo/muduo 工作原理:muduo网络库中Poller类是一个抽象类,用户使用PollPoller或者EPollPoller类。Poller类管理这一个文件描述符fd和Channel指针的映射表channels_,同一个IO线程内所有用户注册的文件描述符都保存在这里,包括定时器或socketfd等。该类

GK_2014的专栏 543

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

C++Muduo网络库初探

在网络I/O中,如果队每个连接都用一个独立的线程来处理,会导致大量的线程资源消耗。因此,出现了一种能够使用一个线程来监听所有网络连接的I/O事件的方法 —— I/O多路复用。常见的I/O复用方法:select、poll、epoll。其中,epoll是一种事件驱动的I/O多路复用的方法。事件驱动的核心是,以事件为连接点,当有IO事件准备就绪时,以事件的形式通知相关线程进行数据读写,进而业务线程可以直接处理这些数据,这一过程的后续操作方,都是被动接收通知,看起来有点像回调操作。

shenfenxihuan的博客 5525

《Muduo网络库:实现Channel通道以及Poller抽象基类》

封装socket地址类型、实现Channel通道、Poller抽象基类、EpollPoller类。理清EventLoop、Channel、Poller三者之间的关系,为实现EventLoop事件循环做准备

2202_75924820的博客 1145

muduo网络库学习(1)

muduo网络库学习 文章目录muduo网络库学习前言一、muduo是什么?二、代码结构1.base库 前言 本章节主要介绍muduo网络库的整体架构! 一、muduo是什么? muduo是一款基于reactor的网络库,整个网络库采用oneloopperthread+threadpool的方法,代码简介,性能优越·。 二、代码结构 1.base库 base库如下(示例): base库主要是为了muduo net库部分封装了一些底层操作,如高性能日志,互斥量,线程,线程池等! ## 2.ne

qq_41409979的博客 2829

muduo网络库学习—EventLoop,Channel,Poller(2)

muduo网络库学习(2) 文章目录muduo网络库学习(2)前言一、oneloop per thread二、EventLoop事件循环流程1.事件循环流程2.读入数据总结 前言 本章主要记录muduo库中的事件循环功能实现即EventLoop类实现。 一、oneloop per thread 为了保证每一个线程只有一个loop循环,muduo在每个线程中创建了一个变量记录EventLoop的指针。 namespace mduo{ namespace net{ // 当前线程EventLoop对象指

qq_41409979的博客 1142
上一篇: muduo网络库——日志篇(一站看懂日志运转流程)
下一篇: 浅浅的分析一手——IOCP
出码弟子
博客等级 码龄5年 968粉丝 9原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值