muduo库的EventLoop类剖析

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

本文地址:http://blog.youkuaiyun.com/freeelinux/article/details/53510541


一:采用的线程通信机制

首先来看一下一个进程(线程)如何通知另一个个等待中的(线程),有三种方法:

1.pipe,使用fd[0] 为读端,fd[1]为写端,半双工。等待线程关注fd[0]的可读事件。

2.socketpair,也有一对文件描述符,可用于双向通信,全双工。

3.eventfd。eventfd是一个比pipe更搞笑的线程间事件通知机制,一方面它比pipe少用一个pipe descriptor,节省了资源。另一方面,eventfd的缓冲区管理也简单的多,全部“buffer"

只有定长8bytes,不想pipe那样可能有不定长的真正buffer。

实际上线程还可以使用条件变量condition来实现消息通信。


muduo采用的就是eventfd,来看一下它的函数:

#include <sys/eventfd.h>

int eventfd(unsigned int initval, int flags);

第一个参数是初始值,一般设置为0,后面是O_NONBLOCK之类的标志。

简单来说eventfd就是一个文件描述符,它引用了一个内核维护的eventfd object,是uint64_t类型,也就是8个字节,可以作为counter。支持read,write,以及有关epoll等操作。

该值不为0,read就可以读了。

关于eventfd更多内容参考我的博客:http://blog.youkuaiyun.com/freeelinux/article/details/53511331

二:EventLoop剖析

EventLoop是整个muduo网络库的核心类,其重要性不言而喻。首先来看一下它的类图:
我们来看一下EventLoop的主要成员:
typedef std::vector<Channel*> ChannelList;

  bool looping_; /* atomic */    //loop循环标志
  bool quit_; /* atomic and shared between threads, okay on x86, I guess. */   //是否退出标志
  bool eventHandling_; /* atomic */     //是否在处理事件标志
  bool callingPendingFunctors_; /* atomic */   //是否调用pendingFunctors标志
  int64_t iteration_;       //迭代器
  const pid_t threadId_;   //当前所属对象线程id
  Timestamp pollReturnTime_;    //时间戳,poll返回的时间戳
  boost::scoped_ptr<Poller> poller_;  //poller对象
  boost::scoped_ptr<TimerQueue> timerQueue_;    //TimerQueue类型对象指针,构造函数中new
  int wakeupFd_;     		//用于eventfd,线程间通信
  // unlike in TimerQueue, which is an internal class,
  // we don't expose Channel to client.
  boost::scoped_ptr<Channel> wakeupChannel_;   //wakeupfd所对应的通道,该通道会纳入到poller来管理
  boost::any context_;    //暂未剖析

  // scratch variables
  ChannelList activeChannels_;   //Poller返回的活动通道,vector<channel*>类型
  Channel* currentActiveChannel_;   //当前正在处理的活动通道

  MutexLock mutex_;
  std::vector<Functor> pendingFunctors_; // GuardedBy mutex_ //本线程或其它线程使用queueInLoop添加的任务,可能是I/O计算任务
然后下面是它的构造函数:
EventLoop::EventLoop()
  : looping_(false),  //表示还未循环
    quit_(false),
    eventHandling_(false),
    callingPendingFunctors_(false),
    iteration_(0),
    threadId_(CurrentThread::tid()),   //赋值真实id
    poller_(Poller::newDefaultPoller(this)),   //构造了一个实际的poller对象
    timerQueue_(new TimerQueue(this)),   //构造一个timerQueue指针,使用scope_ptr管理
    wakeupFd_(createEventfd()),   //创建eventfd作为线程间等待/通知机制
    wakeupChannel_(new Channel(this, wakeupFd_)),   //创建wakeupChannel通道,
    currentActiveChannel_(NULL)
{
  LOG_DEBUG << "EventLoop created " << this << " in thread " << threadId_;
  if (t_loopInThisThread)  //每个线程最多一个EventLoop对象,如果已经存在,使用LOG_FATAL终止abort它。
  {
    LOG_FATAL << "Another EventLoop " << t_loopInThisThread
              << " exists in this thread " << threadId_;
  }
  else
  {
    t_loopInThisThread = this; //this赋给线程局部数据指针,凭借这个这以保证per thread a EventLoop
  }

  //设定wakeupChannel的回调函数,即EventLoop自己的的handleRead函数
  wakeupChannel_->setReadCallback(
      boost::bind(&EventLoop::handleRead, this));
  // we are always reading the wakeupfd
  wakeupChannel_->enableReading();
}
构造函数中创建了一个wakeupFd以及wakeupChannel。之前我们说过Channel类的生命周期由TCPConnection,Acceptor类管理,唯有这个wakeupChannel类不同。这个类和
EventLoop类是组合关系,是EventLoop中的对象,所以它的生命周期由EventLoop负责。实际上,它就是EventLoop类中用来自己给自己通知的一个通道,它利用同是类成员的
wakeupFd_构造,负责提供线程间等待/通知机制。接下俩会具体说。

来看一下EventLoop类的核心函数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_);  //调用poll返回活动的通道,有可能是唤醒返回的
    ++iteration_; 
    if (Logger::logLevel() <= Logger::TRACE)
    {
      printActiveChannels();  //日志登记,日志打印
    }
    // TODO sort channel by priority
    eventHandling_ = true;  //true
    for (ChannelList::iterator it = activeChannels_.begin();
        it != activeChannels_.end(); ++it)  //遍历通道来进行处理
    {
      currentActiveChannel_ = *it;
      currentActiveChannel_->handleEvent(pollReturnTime_);
    }
    currentActiveChannel_ = NULL;   //处理完了赋空
    eventHandling_ = false;  //false
 //I/O线程设计比较灵活,通过下面这个设计也能够进行计算任务,否则当I/O不是很繁忙的时候,这个I/O线程就一直处于阻塞状态。
 //我们需要让它也能执行一些计算任务 
    doPendingFunctors();   //处理用户回调任务
  }

  LOG_TRACE << "EventLoop " << this << " stop looping";
  looping_ = false;
}
wait函数中启用Poller开始wait,等待事件发生,也就是activeChannels_返回。返回后然后挨个处理发生的事件,调用每个返回的Channel的handle_event()函数。

我们来看一下,位于Channel.cc中:
//处理所有发生的事件,如果活着,底层调用handleEventWithGuard
void Channel::handleEvent(Timestamp receiveTime) //事件到来调用handleEvent处理
{
  boost::shared_ptr<void> guard;
  if (tied_)
  {
    guard = tie_.lock();
    if (guard)
    {
      handleEventWithGuard(receiveTime);
    }
  }
  else
  {
    handleEventWithGuard(receiveTime);
  }
}
这里使用了shared_ptr保证线程安全,保证不会调用一个已经销毁了的对象,如果活着(此处具体解释待更新),内部调用handleEventWithGuard()函数处理细节。

看一下这个函数:
//处理所有发生的事件
void Channel::handleEventWithGuard(Timestamp receiveTime)
{
  eventHandling_ = true;
  LOG_TRACE << reventsToString();
  if ((revents_ & POLLHUP) && !(revents_ & POLLIN))  //判断返回事件类型
  {
    if (logHup_)  //如果有POLLHUP事件,输出警告信息
    {
      LOG_WARN << "fd = " << fd_ << " Channel::handle_event() POLLHUP";
    }
    if (closeCallback_) closeCallback_(); //调用关闭回调函数
  }

  if (revents_ & POLLNVAL)  //不合法文件描述符,并没有终止,因为服务器程序要保证一天二十四小时工作。
  {
    LOG_WARN << "fd = " << fd_ << " Channel::handle_event() POLLNVAL";
  }

  if (revents_ & (POLLERR | POLLNVAL))
  {
    if (errorCallback_) errorCallback_();
  }
  if (revents_ & (POLLIN | POLLPRI | POLLRDHUP)) //POLLRDHUP是对端关闭连接事件,如shutdown等
  {
    if (readCallback_) readCallback_(receiveTime);
  }
  if (revents_ & POLLOUT)
  {
    if (writeCallback_) writeCallback_();
  }
  eventHandling_ = false;  //处理完了=false
}
没错,它实际上就是用来处理fd相应的事件,执行相应回调函数的。这些函数我们在fd注册的时候同时注册过,现在事件发生了,就执行相应的回调就行了。
到目前为止,实际上EventLoop完成了它的主要功能了。主要功能就是使用epoll等待事件,事件发生处理事件,然后回去再进行epoll_wait()就可以了,基本功能就是这样。

不过还是要继续往下看,EventLoop还有它额外的功能,此时再回到loop()函数,它会执行到最后一句,是一个函数调用:
  doPendingFunctors();   //
这个函数是用来干什么的呢?其实他是用来让I/O线程执行额外的计算任务的,因为如果没有事件发生,I/O线程会一直处于I/O空闲状态,这个时候我们可以利用I/O线程来执行一些额外的任务。先说一下大概流程:
假设我们有这样的调用:loop->runInLoop(run),说明想让IO线程执行一定的计算任务,此时若是在当前的IO线程,就马上执行run();如果是其他线程调用的,那么就执行
queueInLoop(run),将run异步添加到队列,当loop内处理完事件后,就执行doPendingFunctors(),也就执行到了run();最后想要结束线程的话,执行quit。

我们在看doPendingFunctors()函数之前先来看一下runInLoop()函数:
//顾名思义,在I/O线程中调用某个函数,该函数可以跨线程调用
void EventLoop::runInLoop(const Functor& cb)
{
  //如果是在当前I/O线程中调用,就同步调用cb回调函数
  if (isInLoopThread())
  {
    cb();
  }
  else
  {
  	//否则在其他线程中调用,就异步将cb添加到任务队列当中,以便让EventLoop真实对应的I/O线程执行这个回调函数
    queueInLoop(cb);
  }
}
实际上就是如果是I/O线程主动调用该函数想要执行,那就同步执行该函数。如果是其他线程施加给I/O线程的任务,那么其他线程就需要把回调函数加入I/O线程的队列,等待异步执行。

下面是queueInLoop()函数:
//将任务添加到队列当中,队就是成员pendingFunctors_数组容器
void EventLoop::queueInLoop(const Functor& cb)
{
  {
  MutexLockGuard lock(mutex_);
  pendingFunctors_.push_back(cb);   //添加到任务队列当中
  }
  //如果当前调用queueInLoop调用不是I/O线程,那么唤醒该I/O线程,以便I/O线程及时处理。
  //或者调用的线程是当前I/O线程,并且此时调用pendingfunctor,需要唤醒
  //只有当前I/O线程的事件回调中调用queueInLoop才不需要唤醒
  if (!isInLoopThread() || callingPendingFunctors_)  
  {
    wakeup();
  }
}
加入要执行的事件到队列中后,我们当然希望I/O线程立刻执行该任务,所以立刻调用wakeup函数,唤醒I/O线程,具体实现是这样的:
//唤醒EventLoop
void EventLoop::wakeup()    
{
  uint64_t one = 1;                                                           
  ssize_t n = sockets::write(wakeupFd_, &one, sizeof one);  //随便写点数据进去就唤醒了
  if (n != sizeof one)
  {
    LOG_ERROR << "EventLoop::wakeup() writes " << n << " bytes instead of 8";
  }
}
一看调用了write,且文件描述符为wakeupFd_,我们就知道这是往我们之前的eventfd写一点数据,实现等待/通知,去触发该fd可读。

我们再回头看一下EventLoop的构造函数,有这么两句:
//设定wakeupChannel的回调函数,即EventLoop的handleRead函数
  wakeupChannel_->setReadCallback(
      boost::bind(&EventLoop::handleRead, this));
  // we are always reading the wakeupfd
  wakeupChannel_->enableReading();
这几句可以说是合成了一个eventfd的通道Channel。所以会把它注册如Poller关注的事件里面。结果我找了几分钟没找到(这里有点遗憾,我之前框架都剖析过几次了,这过了一天就忘了),实际上它的这样注册进去的:

它调用Channel的成员函数enableReading()函数:
 void enableReading() { events_ |= kReadEvent; update(); }   //或上事件,就是关注可读事件,注册到EventLoop,通过它注册到Poller中
然后底层实际上是这样子:
void Channel::update()   //更新事件类型
{
  addedToLoop_ = true;
  loop_->updateChannel(this);  //调用loop的,loop再调用poll的注册pollfd
}
下面就不分析了,无非就是注册进epoll事件当中。

所以上面调用了write向eventfd写入几个字节后,EventLoop中poll会返回,然后开始进行事件处理,这次事件是wakeupFd产生的事件,loop()中这样执行:
    currentActiveChannel_ = *it;
      currentActiveChannel_->handleEvent(pollReturnTime_);
实际上调用的就是构造函数中注册的EventLoop::headleRead函数:
//实际上是wakeFd_的读回调函数
void EventLoop::handleRead()
{
  uint64_t one = 1;
  ssize_t n = sockets::read(wakeupFd_, &one, sizeof one);
  if (n != sizeof one)
  {
    LOG_ERROR << "EventLoop::handleRead() reads " << n << " bytes instead of 8";
  }
}
我们看到它从wakeupFd读取了值之后并未处理,因为我们只是把wakeupFd实际上是eventfd作为等待/通知机制实现,这里是为了当我们向EventLoop的queue中也就是
pendingFunctors_这个容器数组加入任务时,通过eventfd通知I/O线程从poll状态退出来执行I/O计算任务。

执行完eventfd的handleRead接下来就是loop()函数中处理任务函数了,就是最后一句:
doPendingFunctors();   //处理用户回调任务
看下它的实现:
// 1. 不是简单的在临界区内依次调用functor,而是把回调列表swap到functors中,这一方面减小了
//临界区的长度,意味着不会阻塞其他线程的queueInLoop(),另一方面也避免了死锁(因为Functor可能再次调用quueInLoop)
// 2. 由于doPendingFunctors()调用的Functor可能再次调用queueInLoop(cb),这是queueInLoop()就必须wakeup(),否则新增的cb可能就不能及时调用了
// 3. muduo没有反复执行doPendingFunctors()直到pendingFunctors为空,这是有意的,否则I/O线程可能陷入死循环,无法处理I/O事件
void EventLoop::doPendingFunctors()
{
  std::vector<Functor> functors;
  callingPendingFunctors_ = true;

  //注意这里的临界区,这里使用了一个栈上变量functors和pendingFunctors交换
  {
  MutexLockGuard lock(mutex_);
  functors.swap(pendingFunctors_); //把它和空vector交换
  }

	//此处其它线程就可以往pendingFunctors添加任务

  //调用回调任务
  //这一部分不用临界区保护
  for (size_t i = 0; i < functors.size(); ++i)
  {
    functors[i]();
  }
  callingPendingFunctors_ = false;
}
它为了防止死锁与兼备高效性所采取的措施注释中已经解释过了,此刻I/O线程就可以顺利的执行这么些任务了。




基于C++11重写muduo库 邮箱:2877944240@qq.com 源码:基于C++11重写muduo库 如有转载,请标明出处,谢谢! 文章目录预备知识muduo库简介muduo库的优点muduo和libeventone loop per process言归正传muduo的模型设计Reactor模型muduo库的Reactor模型one (evnet)loop per threadC++11重写muduo核心类代码muduo库的核心类代码辅助类NoncopyableTimestampLoggersocketInetAddres 阅读详情

相关推荐

深入理解 Event Loop事件循环机制

Event Loop事件循环机制 事件循环机制(Event Loop)是全面了解javascript代码执行顺序绕不开的一个重要知识点。 为什么会有事件循环机制? javascript从诞生之日起就是一门单线程的非阻塞的脚本语言。这是由其最初的用途来决定的:与浏览器交互。 首先等来了解js为什么是单线程的吧! 首先要明白线程的概念? 进程是资源分配的最小单位,线程是进程的一部分,是cpu调度的基本单位; 了解浏览器 浏览器中,每次打开一个tab页面,其实就是新开了一个进程,在这个进程中,还有js引擎线程、ui渲染线程、http请求线程。 这里面还可深入了解js引擎线程、ui渲染线程、http请

muduo源码分析--事件回调层次是怎么传递的Tcpserver Channel TcpConnection

muduo源码分析--事件回调层次是怎么传递的Tcpserver Channel TcpConnection muduo库中的源码并不是很多,但是回调的处理非常巧妙,这里从事件激活(某个套接字上可读/可写)以后这个层次看回调怎么被调用的。首先从最大的EventLoop说起: EventLoop中拥有事件链表(每一个元素都是Channel),在loop函数中调用epoll_wait系统调用...

唧唧歪歪专题博客 205

Sharding-JDBC 系列专题 - 第九篇:高可用性与集群管理

高可用性是指系统在面对故障(如数据库宕机、网络中断)时,能够快速恢复并继续提供服务。Sharding-JDBC 和 Sharding-Proxy 提供了多种机制支持高可用性,同时需要结合底层数据库的高可用方案(如 MySQL 主从复制、MHA)来实现。高可用性和集群管理是 Sharding-JDBC 和 Sharding-Proxy 在生产环境中成功应用的关键。本篇详细介绍了数据库高可用方案、Sharding-JDBC 的故障切换机制、Sharding-Proxy 的集群部署,以及分布式治理功能。

资深架构师分享:深入剖析软件架构,探讨云计算、微服务、大数据等前沿技术。分享实践经验,促进技术交流,共同构建卓越系统。 1393

Linux C++编程之Eventloop

从muduo分离出来的eventloop.

noevil的博客 963

Xilinx Vitis 2020.1中Microblaze SREC SPI Bootloader初始化失败的调试与修复指南

本文详细解析了在Xilinx Vitis 2020.1开发环境中,Microblaze SREC SPI Bootloader初始化失败的常见原因与解决方案。核心问题通常源于Flash上电时序或软件配置,如`serial_flash_family`参数未生效、链接脚本设置不当或BRAM初始化文件选择错误。文章提供了从硬件配置检查、工程设置修正到代码级添加重试机制的完整调试路径,帮助开发者快速定位并修复启动故障,确保系统稳定启动。

weixin_29163857的博客 188

Ansys Zemax | 公差分析中的补偿器优化:如何提升系统良率?

本文深入探讨了在Ansys Zemax公差分析中如何有效设置与优化补偿器以提升系统良率。文章指出,补偿器是连接理想设计与现实生产的关键,通过模拟实际装配中的调整(如后焦距),能显著改善公差分析的“悲观”预测。详细介绍了补偿器的添加方法、多补偿器策略、评价函数设计以及通过灵敏度分析和蒙特卡洛档案优化补偿效果,旨在帮助光学工程师制定更符合生产实际的公差策略,从而有效提升产品良率。

weixin_28419039的博客 623

Linux事件循环阻塞,EventLoop事件循环

我们常见的JavaScript运行时(runtime)有两个,一个是浏览器环境,一个是Node.js环境JavaScript 事件循环机制分为浏览器和 Node 事件循环机制,两者的实现技术不一样。浏览器 Event Loop 是 HTML 中定义的规范,Node Event Loop 是由 libuv 库实现浏览器的事件循环机制一、为什么JavaScript是单线程?背景JavaScript的单...

weixin_39954908的博客 340

MATLAB实战:5步搞定GNSS单点定位解算(附完整代码)

本文提供了一份基于MATLAB的GNSS单点定位实战指南。通过5个核心步骤,详细讲解了如何从卫星坐标和伪距观测值出发,利用最小二乘迭代算法,构建并实现一个完整的单点定位解算器。文章附有完整代码,并涵盖精度评估与结果分析,适合GNSS初学者和MATLAB用户快速上手实践。

a2b3c4d5e的博客 250

linux事件循环机制eventloop,理解EventLoop(事件循环)

EventLoop是什么?EventLoop是一个执行模型,在不同的地方有不同的实现。浏览器和NodeJs基于不同的技术实现了各自的EventLoop。JS是单线程语言,JS的Event Loop是JS的执行机制。深入了解JS的执行,就等于深入了解JS里的event loop。浏览器的Event Loop是在html5的规范中明确定义。NodeJS的Event Loop是基于libuv实现的。可以...

weixin_33945759的博客 1092

AI Agent辅助企业财务分析与预测

企业财务分析与预测是企业管理中至关重要的环节,准确的财务分析与预测能够帮助企业管理者做出明智的决策,合理规划资源,降低风险。然而,传统的财务分析与预测方法往往依赖于人工经验和简单的统计模型,存在效率低、准确性差等问题。随着人工智能技术的飞速发展,AI Agent作为一种智能的软件实体,能够自主地感知环境、做出决策并采取行动,为企业财务分析与预测带来了新的解决方案。

AI天才研究院 922

数字信号处理----数字上变频和下变频都FPGA实现

** 数字信号处理基础----信号下变频和解调 一、 任务介绍 数字上变频器(DUC)和数字下变频器(DDC)广泛应用于通信系统,用于信号采样速率的转换。当信号从基带转换至中频( IF )带,需要使用数字上变频器。而数字下变频器是用于将信号从中频( IF )带转换为基带。DUC和DDC通常包括使用混频器进行频率转换,此外还有采样率转换。DUC或DDC的结构主要取决于所需要的转换率。例如,WiMAX (全球互通微波接入)系统典型的转换率为8—10阶。对于如此低的转换率,DUC和DDC只需采用FIR滤波器架构。

Born_toward的博客 3万+

muduo库EventLoop类源码解析

EventLoop也称为事件循环,从调用epoll_wait系统调用捞取到就绪事件到再次阻塞到epoll_wait上(阻塞到内核中的就绪队列上了)称为一次事件循环。muduo库的EventLoop在捞取完就绪事件后会立即执行就绪事件的回调。EventLoop还有一个线程安全的任务队列。在执行完就绪事件的回调后不会立即阻塞到epoll_wait上,他会在执行完任务队列中的任务后在阻塞到就绪队列上。为什么要搞一个线程安全的任务队列呢?muduo库的设计思想是。

2301_79796701的博客 1630

基于哈夫曼编码的图像加密算法matlab仿真

哈夫曼编码是一种常用的无损数据压缩算法,其基本思想是根据字符出现的频率构造一棵哈夫曼树,将出现频率高的字符编码为短码,出现频率低的字符编码为长码,从而实现数据的压缩。在图像加密领域,哈夫曼编码也被广泛应用于图像加密算法中,基于哈夫曼编码的图像加密算法具有加密速度快、加密强度高等优点。基于哈夫曼编码的图像加密算法的基本思路是将待加密的图像分为多个小块,对每个小块进行哈夫曼编码,然后将哈夫曼编码后的结果进行异或运算,最后将异或运算后的结果重新编码,以实现图像加密。具体步骤如下:将待加密的图像分成多个小块。

平时工作较为繁忙,私信消息一般晚上回复,谢谢大家~~ 264

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

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

m0_73537205的博客 2118

使用everything出现mem_virtual_alloc(): Fatal Error: out of memory解决方案

本文主要介绍了使用everything出现mem_virtual_alloc(): Fatal Error: out of memory解决方案,希望能对使用everything的同学们有所帮助。 文章目录 1. 问题描述 2. 解决方案

weixin_43178406的博客 1万+

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Muduo库源码剖析(四)——EventLoop类

顾名思义每个线程只能有一个EventLoop对象,因此EventLoop的构造函数会检查当前线程是否已经创建了其他EventLoop对象,遇到错误就终止程序(LOG_FATAL)。:与其他进程间通信机制相比,如管道、消息队列等,eventfd() 的开销更小,因为它不需要。:eventfd() 可以用于在不同线程之间传递信号,使得它在多线程应用程序中非常有用。EventLoop对象的生命期通常和其所属的线程一样长,它不必是。它在主线程创建了EventLoop对象,却试图在另一个线程调用其。

qq_42120843的博客 467

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

muduo_net库源码分析(25)

TCP网络编程本质     TCP网络编程最本质是的处理三个半事件         连接建立:服务器accept(被动)接受连接,客户端connect(主动)发起连接         连接断开:主动断开(close、shutdown),被动断开(read返回0)         消息到达:文件描述符可读         消息发送完毕:这算半个。对于低流量的服务,可不必关心这个事件;这里的

Riak, Spark, Golang, Erlang, 云存储, 云计算, 数据挖掘 2462

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

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

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

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库代码剖析 : ThreadPool

本章我们将把EventLoop和C++11的thread结合, 实现Thread / EventLoopThread / EventLoopThreadPool.

tianmu_sama的博客 1096

EventLoopThreadPool Class源码阅读(muduo::net)

#EventLoopThreadPool Class`源码阅读(muduo::net) 如果有问题,欢迎交流!!! 数据成员 EventLoop* baseLoop_ EventLoopTreadPool Class对象所在loop。有些成员函数在执行前要检查当前是不是在baseLoop_所在线程 string name_ 用于给一众EventLoopThread命名 int numThreads_ 创建的线程数量 next_ 下一个EventLoop对象 std::vec

Wendell_0的博客 161

【Muduo源码剖析笔记】 网络库之TcpServer

Muduo库源码阅读笔记

LeeEdgar的博客 158

长文梳理Muduo库核心代码及优秀编程细节剖析

长文梳理Muduo库核心代码(Multi-Reactor架构代码)及优秀编程细节思想剖析。

T_Solotov的博客 1万+

[muduo网络库]——muduo网络库精华总结(剖析muduo网络库核心部分、设计思想)

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

m0_73537205的博客 2826

muduo网络库剖析——事件循环EventLoop类

基于c++的muduo网络库开源项目深度剖析!超级加分的简历项目,期待您的讨论~

weixin_62021811的博客 1368

Muduo库代码剖析 : EventLoop

EventLoop类似于Reactor模型中的**反应堆**(Reactor)和**事件分发器**(Demultiplex)的合并, 其目的在于高效的接收事件, 并正确分配给对应的事件处理器.

tianmu_sama的博客 882

[muduo网络库]——muduo库TcpConnection类,万字总结(剖析muduo网络库核心部分、设计思想)

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

m0_73537205的博客 4202
上一篇: 最高效的进(线)程间通信机制--eventfd
下一篇: 矩阵的最小路径和 [DP]
FreeeLinux
博客等级 码龄11年 421粉丝 356原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值