EventLoop再分析之IO线程(29)

【Hard Python】【第二章-异步IO】2、异步任务在事件循环中的执行 接续第一话的内容,事件循环在创建之后,又是如何运行协程任务以及异步IO任务的? ​ 由asyncio.run的代码可知,loop.run_until_complete是运行协程的方法。其定义如下: # base_events.py class BaseEventLoop(events.AbstractEventLoop): def run_until_complete(self, future): self._check_closed() self._check_ru 阅读详情


EventLoop IO 线程的简单描述






进程(线程)wait/notify

  • pipe

  • socketpair

  • eventfd
    eventfd 是一个比 pipe 更高效的线程间事件通知机制,一方面它比 pipe 少用一个 file descripor,节省了资源;另一方面,eventfd 的缓冲区管理也简单得多,全部“buffer” 只有定长8 bytes,不像 pipe 那样可能有不定长的真正 buffer。



EventLoop头文件

eventloop.h

这里的eventloop 和 “muduo_net库源码分析(26-1)”里面的内容有些不一样,这里的eventloop加了一些内容

在阅读eventloop时,要注意runInLoop和queueInloop这两个函数,wakeup()也得注意


// Copyright 2010, Shuo Chen.  All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
 
// Author: Shuo Chen (chenshuo at chenshuo dot com)
//
// This is a public header file, it must only include public header files.
 
#ifndef MUDUO_NET_EVENTLOOP_H
#define MUDUO_NET_EVENTLOOP_H
 
#include <vector>
 
#include <boost/noncopyable.hpp>
#include <boost/scoped_ptr.hpp>
 
#include <muduo/base/Mutex.h>
#include <muduo/base/Thread.h>
#include <muduo/base/Timestamp.h>
#include <muduo/net/Callbacks.h>
#include <muduo/net/TimerId.h>
 
namespace muduo
{
namespace net
{
 
class Channel;
class Poller;
class TimerQueue;
///
/// Reactor, at most one per thread.
///
/// This is an interface class, so don't expose too much details.
class EventLoop : boost::noncopyable
{
 public:
  /* IO事件回调函数,就像“监听socketfd” ,我们可以把 监听socketfd 的处理函数 做成一个回调函数,
  把这个函数放到pendingFunctors_中,然后由IO线程自己回调处理*/
  typedef boost::function<void()> Functor;
 
  EventLoop();
  ~EventLoop();  // force out-line dtor, for scoped_ptr members.
 
  ///
  /// Loops forever.
  ///
  /// Must be called in the same thread as creation of the object.
  ///
  void loop();
 
  void quit();
 
  ///
  /// Time when poll returns, usually means data arrivial.
  ///
  Timestamp pollReturnTime() const { return pollReturnTime_; }
 
  /// Runs callback immediately in the loop thread.
  /// It wakes up the loop, and run the cb.
  /// If in the same loop thread, cb is run within the function.
  /// Safe to call from other threads.
  void runInLoop(const Functor& cb);
  /// Queues callback in the loop thread.
  /// Runs after finish pooling.
  /// Safe to call from other threads.
  void queueInLoop(const Functor& cb);
 
  // timers
 
  ///
  /// Runs callback at 'time'.
  /// Safe to call from other threads.
  ///
  TimerId runAt(const Timestamp& time, const TimerCallback& cb);
  ///
  /// Runs callback after @c delay seconds.
  /// Safe to call from other threads.
  ///
  TimerId runAfter(double delay, const TimerCallback& cb);
  ///
  /// Runs callback every @c interval seconds.
  /// Safe to call from other threads.
  ///
  TimerId runEvery(double interval, const TimerCallback& cb);
  ///
  /// Cancels the timer.
  /// Safe to call from other threads.
  ///
  void cancel(TimerId timerId);
 
  // internal usage
  void wakeup();
  void updateChannel(Channel* channel);     // 在Poller中添加或者更新通道
  void removeChannel(Channel* channel);     // 从Poller中移除通道
 
  void assertInLoopThread()
  {
    if (!isInLoopThread())
    {
      abortNotInLoopThread();
    }
  }
  bool isInLoopThread() const { return threadId_ == CurrentThread::tid(); }
 
  bool eventHandling() const { return eventHandling_; }
 
  static EventLoop* getEventLoopOfCurrentThread();
 
 private:
  void abortNotInLoopThread();
  void handleRead();  // waked up
  void doPendingFunctors();
 
  void printActiveChannels() const; // DEBUG
 
  typedef std::vector<Channel*> ChannelList;
 
  bool looping_; /* atomic */
  bool quit_; /* atomic */
  bool eventHandling_; /*事件处理函数状态 atomic */
  bool callingPendingFunctors_; /*是否处于IO回调函数的处理状态中 atomic */
  const pid_t threadId_;        // 当前对象所属线程ID
  Timestamp pollReturnTime_;
  boost::scoped_ptr<Poller> poller_;
  boost::scoped_ptr<TimerQueue> timerQueue_;
 
/*****
 
wakeupFd_ : 这个描述符主要是未了
 
****/
  int wakeupFd_;                // 用于eventfd所创建的文件描述符
  // unlike in TimerQueue, which is an internal class,
  // we don't expose Channel to client.
  /*
  wakeupChannel 和EventLoop 是组合的关系,生命周期由EventLoop管理
  一定要看下面的内容,如果不明白下面的内容的话,代码是很难懂的(个人观点,高手勿喷!!^V^)
  当其他线程跟IO线程通信时, 他们使用的是同一个通道(wakeupChannel_) , 当其他线程wakeup(write)时,
poll会返回。然后IO执行handleRead,但是主要的回调函数不是handleRead ,而是pendingFunctors_里面的函数,
hadnleRead只是为了不被多次触发而已,也就是说其他线程要想IO线程执行他们所需的函调函数,只要把回调函数
注入到pendingFunctors_,然后wakeup(write)唤醒一下。
  当然IO线程也可以和IO线程进行通信,但是情况有些不一样,具体哪些不同,大家自己看着办吧!
 
  注: IO线程就是EventLoop所属的线程
  */
  boost::scoped_ptr<Channel> wakeupChannel_;  //wakeupFd_所对应的通道 该通道将会纳入poller_来管理
  ChannelList activeChannels_;      // Poller返回的活动通道
  Channel* currentActiveChannel_;   // 当前正在处理的活动通道
  MutexLock mutex_;
  // IO线程 回调函数容器
  std::vector<Functor> pendingFunctors_; // @BuardedBy mutex_
};
 
}
}
#endif  // MUDUO_NET_EVENTLOOP_H


EventLoop源文件

这里的eventloop 和 “muduo_net库源码分析(26-1)”里面的内容有些不一样,这里的eventloop加了一些内容,具体情况可以回去看一看

// Copyright 2010, Shuo Chen.  All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
 
// Author: Shuo Chen (chenshuo at chenshuo dot com)
 
#include <muduo/net/EventLoop.h>
 
#include <muduo/base/Logging.h>
#include <muduo/net/Channel.h>
#include <muduo/net/Poller.h>
#include <muduo/net/TimerQueue.h>
 
//#include <poll.h>
#include <boost/bind.hpp>
 
#include <sys/eventfd.h>
 
using namespace muduo;
using namespace muduo::net;
 
namespace
{
// 当前线程EventLoop对象指针
// 线程局部存储
__thread EventLoop* t_loopInThisThread = 0;
 
const int kPollTimeMs = 10000;
 
int createEventfd()
{
  int evtfd = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
  if (evtfd < 0)
  {
    LOG_SYSERR << "Failed in eventfd";
    abort();
  }
  return evtfd;
}
 
}
 
EventLoop* EventLoop::getEventLoopOfCurrentThread()
{
  return t_loopInThisThread;
}
 
EventLoop::EventLoop()
  : looping_(false),
    quit_(false),
    eventHandling_(false),
    callingPendingFunctors_(false),
    threadId_(CurrentThread::tid()),
    poller_(Poller::newDefaultPoller(this)),
    timerQueue_(new TimerQueue(this)),
    wakeupFd_(createEventfd()), //创建 wakeupFd_
    wakeupChannel_(new Channel(this, wakeupFd_)), //创建wakeupFd_对应的channel
    currentActiveChannel_(NULL)
{
  LOG_TRACE << "EventLoop created " << this << " in thread " << threadId_;
  // 如果当前线程已经创建了EventLoop对象,终止(LOG_FATAL)
  if (t_loopInThisThread)
  {
    LOG_FATAL << "Another EventLoop " << t_loopInThisThread
              << " exists in this thread " << threadId_;
  }
  else
  {
    t_loopInThisThread = this;
  }
 
  // 设定wakeupChannel的回调函数
  wakeupChannel_->setReadCallback(
      boost::bind(&EventLoop::handleRead, this));
  // we are always reading the wakeupfd
  //纳入poller来管理
  wakeupChannel_->enableReading();
}
 
EventLoop::~EventLoop()
{
  ::close(wakeupFd_);
  t_loopInThisThread = NULL;
}
 
// 事件循环,该函数不能跨线程调用
// 只能在创建该对象的线程中调用
void EventLoop::loop()
{
  assert(!looping_);
  // 断言当前处于创建该对象的线程中
  assertInLoopThread();
  looping_ = true;
  quit_ = false;
  LOG_TRACE << "EventLoop " << this << " start looping";
 
  //::poll(NULL, 0, 5*1000);
  while (!quit_)
  {
    activeChannels_.clear();
    pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);
    //++iteration_;
    if (Logger::logLevel() <= Logger::TRACE)
    {
      printActiveChannels();
    }
    // TODO sort channel by priority
    eventHandling_ = true;
    for (ChannelList::iterator it = activeChannels_.begin();
        it != activeChannels_.end(); ++it)
    {
      currentActiveChannel_ = *it;
      currentActiveChannel_->handleEvent(pollReturnTime_);
    }
    currentActiveChannel_ = NULL;
    eventHandling_ = false;
    // 让IO线程也能执行一些计算任务
    doPendingFunctors();
  }
 
  LOG_TRACE << "EventLoop " << this << " stop looping";
  looping_ = false;
}
 
// 该函数可以跨线程调用
void EventLoop::quit()
{
  quit_ = true;
  //如果不是由当前IO线程调用的,那么我们还要唤醒IO线程
  if (!isInLoopThread())
  {
    wakeup();
  }
}
/*
sublime 注释的好方法,都是苦力活
===========         ===================               ======         ===================
||        |||      |||=================             =                         {}
||        ****      ||                             =                          {}
||       ****       ||                            =                           {}
|||||||||           ||=================              =  = = =                 {}
||      \\\         ||==================                      ///             {}
||        \\        ||                                      = //              {}
                    //                                        //              {}
||        \\\       || ==================                    //               {}
||          \\      ||===================           =========                 {}
*/
// 在I/O线程中注册某个回调函数,该函数可以跨线程调用
void EventLoop::runInLoop(const Functor& cb)
{
  if (isInLoopThread())
  {
    // 如果是当前IO线程调用runInLoop,则同步调用cb
    cb();
  }
  else
  {
    // 如果是其它线程调用runInLoop,则异步地将cb添加到队列,让EventLoop线程来执行这个cb()函数
    queueInLoop(cb);
  }
}
 
// 把IO回调函数注册到pendingFunctors_中
void EventLoop::queueInLoop(const Functor& cb)
{
  {
    // 保护临界区
  MutexLockGuard lock(mutex_);
    // 将任务添加到任务队列中
  pendingFunctors_.push_back(cb);
  }
 
  // 1.--->调用queueInLoop的线程不是IO线程需要唤醒,以便IO线程及时处理任务
  // 2.--->或者调用queueInLoop的线程是IO线程,并且此时正在调用pending functor,需要唤醒
   /*   如果我们不唤醒,那么下一次poll就没法获取该cb的触发事件,这样就会使事件处理延迟了 ,请参照loop函数 */
  // 3---->只有IO线程的事件回调中调用queueInLoop才不需要唤醒 ,因为handleEvent执行后,
  //下面接着执行doPendingFunctors
  if (!isInLoopThread() || callingPendingFunctors_)
  {
    wakeup();
  }
}
 
TimerId EventLoop::runAt(const Timestamp& time, const TimerCallback& cb)
{
  return timerQueue_->addTimer(cb, time, 0.0);
}
 
TimerId EventLoop::runAfter(double delay, const TimerCallback& cb)
{
  Timestamp time(addTime(Timestamp::now(), delay));
  return runAt(time, cb);
}
 
TimerId EventLoop::runEvery(double interval, const TimerCallback& cb)
{
  Timestamp time(addTime(Timestamp::now(), interval));
  return timerQueue_->addTimer(cb, time, interval);
}
 
void EventLoop::cancel(TimerId timerId)
{
  return timerQueue_->cancel(timerId);
}
 
void EventLoop::updateChannel(Channel* channel)
{
  assert(channel->ownerLoop() == this);
  assertInLoopThread();
  poller_->updateChannel(channel);
}
 
void EventLoop::removeChannel(Channel* channel)
{
  assert(channel->ownerLoop() == this);
  assertInLoopThread();
  if (eventHandling_)
  {
    assert(currentActiveChannel_ == channel ||
        std::find(activeChannels_.begin(), activeChannels_.end(), channel) == activeChannels_.end());
  }
  poller_->removeChannel(channel);
}
 
void EventLoop::abortNotInLoopThread()
{
  LOG_FATAL << "EventLoop::abortNotInLoopThread - EventLoop " << this
            << " was created in threadId_ = " << threadId_
            << ", current thread id = " <<  CurrentThread::tid();
}
 
/*
 
 
唤醒等待的线程
*/
void EventLoop::wakeup()
{
  uint64_t one = 1;
  //ssize_t n = sockets::write(wakeupFd_, &one, sizeof one);
  /*向wakeupFd_中写入数据*/
  ssize_t n = ::write(wakeupFd_, &one, sizeof one);
  if (n != sizeof one)
  {
    LOG_ERROR << "EventLoop::wakeup() writes " << n << " bytes instead of 8";
  }
}
 
// IO事件回调函数
void EventLoop::handleRead()
{
  uint64_t one = 1;
  //ssize_t n = sockets::read(wakeupFd_, &one, sizeof one);--->>muduolib
  ssize_t n = ::read(wakeupFd_, &one, sizeof one); // 这是先这样设定
  if (n != sizeof one)
  {
    LOG_ERROR << "EventLoop::handleRead() reads " << n << " bytes instead of 8";
  }
}
 
/*IO线程的回调函数集 处理函数*/
void EventLoop::doPendingFunctors()
{
  std::vector<Functor> functors;
  /*正在处于IO线程处理函数中*/
  callingPendingFunctors_ = true;
 
  {
    // 加锁
  MutexLockGuard lock(mutex_);
  //交换,顺带将pendingFunctors清空
  functors.swap(pendingFunctors_);
  }
 
  for (size_t i = 0; i < functors.size(); ++i)
  {
    functors[i]();
  }
  /*清空IO线程回调处理函数中 的状态*/
  callingPendingFunctors_ = false;
}
 
void EventLoop::printActiveChannels() const
{
  for (ChannelList::const_iterator it = activeChannels_.begin();
      it != activeChannels_.end(); ++it)
  {
    const Channel* ch = *it;
    LOG_TRACE << "{" << ch->reventsToString() << "} ";
  }
}



测试程序源代码

#include <muduo/net/EventLoop.h>
//#include <muduo/net/EventLoopThread.h>
//#include <muduo/base/Thread.h>
 
#include <stdio.h>
 
using namespace muduo;
using namespace muduo::net;
 
EventLoop* g_loop;
int g_flag = 0;
 
void run4()
{
  printf("run4(): pid = %d, flag = %d\n", getpid(), g_flag);
  g_loop->quit();
}
 
void run3()
{
  printf("run3(): pid = %d, flag = %d\n", getpid(), g_flag);
 
  g_loop->runAfter(3, run4);
  g_flag = 3;
}
 
void run2()
{
  printf("run2(): pid = %d, flag = %d\n", getpid(), g_flag);
    /*在IO线程中直接调用queueInLoop,并且if (!isInLoopThread() || callingPendingFunctors_)时,
  不用唤醒(wakeup())
    */
  g_loop->queueInLoop(run3);
}
 
void run1()
{
  g_flag = 1;
  printf("run1(): pid = %d, flag = %d\n", getpid(), g_flag);
  /*在IO线程中,调用runInLoop时,不用经过queueInLoop()就可以直接调用Functors*/
  g_loop->runInLoop(run2);
  g_flag = 2;
}
 
int main()
{
  printf("main(): pid = %d, flag = %d\n", getpid(), g_flag);
 
  EventLoop loop;
  g_loop = &loop;
 
  loop.runAfter(2, run1);
  loop.loop();
  printf("main(): pid = %d, flag = %d\n", getpid(), g_flag);
}


程序输出

ubuntu@ubuntu-virtual-machine:~/29/jmuduo$ ../build/debug/bin/reactor_test05
main(): pid = 28193, flag = 0
20131022 01:36:15.466526Z 28193 TRACE updateChannel fd = 4 events = 3 - EPollPoller.cc:104
20131022 01:36:15.467517Z 28193 TRACE EventLoop EventLoop created 0xBFEDB874 in thread 28193 - EventLoop.cc:62
20131022 01:36:15.467586Z 28193 TRACE updateChannel fd = 5 events = 3 - EPollPoller.cc:104
20131022 01:36:15.467898Z 28193 TRACE loop EventLoop 0xBFEDB874 start looping - EventLoop.cc:94
20131022 01:36:17.469365Z 28193 TRACE poll 1 events happended - EPollPoller.cc:65
20131022 01:36:17.482824Z 28193 TRACE printActiveChannels {4: IN }  - EventLoop.cc:257
20131022 01:36:17.483170Z 28193 TRACE readTimerfd TimerQueue::handleRead() 1 at 1382405777.482909 - TimerQueue.cc:62
run1(): pid = 28193, flag = 1
run2(): pid = 28193, flag = 1
run3(): pid = 28193, flag = 2
20131022 01:36:20.483953Z 28193 TRACE poll 1 events happended - EPollPoller.cc:65
20131022 01:36:20.484074Z 28193 TRACE printActiveChannels {4: IN }  - EventLoop.cc:257
20131022 01:36:20.484119Z 28193 TRACE readTimerfd TimerQueue::handleRead() 1 at 1382405780.484109 - TimerQueue.cc:62
run4(): pid = 28193, flag = 3
20131022 01:36:20.484229Z 28193 TRACE loop EventLoop 0xBFEDB874 stop looping - EventLoop.cc:119
main(): pid = 28193, flag = 3
ubuntu@ubuntu-virtual-machine:~/29/jmuduo$


Netty-自定义线程池处理长耗时业务 在Netty中,EventLoop 和 EventExecutorGroup 提供的普通任务不能解决长耗时业务处理引起的 I/O 线程阻塞问题,究其原因:在 Netty 中,一个 channel 在它的生命中期内只注册于一个 EventLoop,而一个 EventLoop 在它的生命周期内只和一个线程绑定,一个给定的 channel 的 I/O 操作都是由相同的线程进行处理。为此不能直接在该线程上进行长耗时业务处理。为了解决这个问题,通常的做法是:自定义线程池,把长耗时的业务都丢到线程池中去处理。 阅读详情

相关推荐

Nornir自动化简单介绍-网络设备信息收集

作为网工,学会通过执行脚本,可以自动化的完成日常一些设备相关配置或信息收集等工作,可以说是事半功倍。 Nornir就是这样一个自动化网络运维工具。按照官网上的说明: Nornir是用python编写的自动化框架,可与python一起使用。Nornir旨在通过提供一个纯Python框架来解决这些问题。 详细参考: Github GitHub - nornir-automation/nornir: Pluggable multi-threaded framework with inventory .

Gun&rose 7804

pyshark报错,Error on reading from the event loop self pipe

在脚本最后一行补上,对FileCapture生成的对象cap进行关闭,cap.close(),就不会报错了。

amber_o0k的博客 1973

保姆级教程:用BMS的HVIL检测电路,手把手教你排查高压互锁故障(附PWM/直流源方案对比)

本文详细解析了BMS中的HVIL检测电路,通过对比直流源与PWM两种方案,提供了一套系统化的高压互锁故障诊断流程。从电路原理到实战技巧,帮助维修人员精准定位故障根源,提升电动汽车高压系统的安全性和可靠性。

weixin_29300241的博客 390

Netty---EventLoop详解

《Netty,redis,zookeeper高并发实战》读书笔记 一,Netty实践demo public class NettyDiscardServer { privateint serverPort=8080; ServerBootstrap b=new ServerBootstrap(); public NettyDiscardServer(int p...

阿七 1129

当迁移学习遇见室内定位:基于UJIIndoorLoc的跨场景适配实践

本文探讨了迁移学习在跨建筑室内定位中的应用,重点解析了UJIIndoorLoc数据集的特性及其在解决设备差异和环境变化中的关键作用。通过特征工程、迁移学习策略和小样本微调技术,显著提升了定位模型的跨场景适配能力,为实际部署提供了有效解决方案。

weixin_29038155的博客 394

中国30米逐年植被净初级生产力(NPP)数据集(1982年至今CASA模型)

1.3 气温、降水、地表太阳辐射数据 气温和降水数据月度空间插值数据是基于全国2400多个站点的日观测数据,在计算各气象要素月值的基础上,基于Anusplin软件插值生成的1982年以来各月度30米分辨率气温和降水栅格数据,插值过程中引入高程作为协变量。气温单位为℃,降水单位为mm。1.2 土地覆盖数据 土地覆盖数据来源于1985 年-2020年全球30米精细地表覆盖动态监测产品,该数据集包含29个地表覆盖类型,进行拼接、裁剪、重分类和重采样处理,获得8期250米分辨率的植被类型数据。

地球资源数据云的官方博客 2839

区别下IO多路复用,reactor,事件循环(EventLoop),Epoll这几个的概念以及之间的关系

IO多路复用是一种底层操作系统提供的机制(如selectpollepoll),用于检测多个文件描述符是否就绪。epoll是Linux平台上的一种高性能IO多路复用实现。Reactor是采用IO多路复用技术实现的一种架构设计模式,用于事件驱动的异步处理。事件循环(EventLoop)是在应用层的实现,用于不断地检测和调度事件,典型地融合了Reactor设计思想。

2301_80355452的博客 631

【Docker】如何让docker容器正常使用nvidia显卡

描述了如何在docker容器中使用英伟达显卡的过程和问题解决方法。

NameLoadingFailed的博客 8372

I/O模型、Libuv和Eventloop

一、I/O模型 ①常见的IO模型:Linux(UNIX)操作系统中的网络IO模型为例 Blocking I/O 同步阻塞IO Non-blocking I/O 同步非阻塞IO I/O Multiplexing IO多路复用 Signal-blocking I/O 信号驱动IO Asynchronous I/O 异步IO ②基本概念的定义: IO 指的是输入输出,通常指数据在内...

weixin_34166472的博客 571

KUKA .RSI 软件包

KUKA RSI软件包通过XML文件使得机器人与外部设备(传感器通讯)通讯成为了可能,但仍存在诸多限制,该模块最多允许9台设备进行连接,有限的IO口,匮乏的功能,并且需要投入大量精力琢磨相关说明文档。为此开发人员针对KUKA实时通讯,做了一些开源的接口使得上位机与机械臂的通讯更为简单。

huhu7777的博客 3290

(9-2)离线强化学习:离线强化学习的主流方法

本文介绍了三种离线强化学习的主流约束策略优化方法:BCQ、CQL和Fisher-BRC,并通过CartPole环境进行性能比较。BCQ利用生成模型约束动作选择并引入扰动网络;CQL通过正则化Q值抑制对未见状态-动作对的高估;Fisher-BRC使用Fisher散度正则化策略分布。实验结果表明,三种方法各有优势,其中CQL表现最为稳定。文章还指出,为获得更好的性能,需要更大的数据集和更长的训练轮次。这些方法通过限制策略优化范围,有效提高了离线强化学习的稳定性和泛化能力。

码农三叔 1536

29.muduo学习笔记之net_EventLoop.{h&cc}

1. 说明 一个EventLoop类,任何一个线程,只要创建并运行了EventLoop,都称之为IO线程(为了方便使用,定义了EventLoopThread类,封装了IO线程) 使用了Channel,Poller,TimerQueue,Timestamp类 noncopyable 2. 变量 类型定义 Functor ChannelList looping_; /* atomic ...

LvPartner的博客 314

【AI大模型应用开发】1.0 Prompt Engineering(提示词工程)- 典型构成、原则与技巧,代码中加入Prompt

Prompt典型构成、原则与技巧,以及OpenAI API重要参数解释,助你轻松入门Prompt

同学小张的博客 2580

muduo网络库学习之EventLoop(四):EventLoopThread 类、EventLoopThreadPool 类

1、Socket 操作封装 Endian.h 封装了字节序转换函数(全局函数,位于muduo::net::sockets名称空间中)。 SocketsOps.h/ SocketsOps.cc 封装了socket相关系统调用(全局函数,位于muduo::net::sockets名称空间中)。 Socket.h/Socket.cc(Socket类) 用RAI

Meditation 6045

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

(P29)muduo_base库源码分析:进程(线程)wait/notify

文章目录1.进程(线程)wait/notify 1.进程(线程)wait/notify 一个线程通知一个等待中的线程,方法有3个: 方法1:pipe (1)fd[0]对应管道的读端,fd[1]对应管道的写端,fd[0]只能用于读,不能用于写,fd[1]只能用于写,不能用于读,这意味着管道是单向的; (2)等待线程等待fd[0]的可读事件,通知线程只要往fd[1]写入一个数据,fd[0]就变的可读了,等待线程就获得通知,就被唤醒了; 方法2:socketpair (1)也有一对fd,任意一个fd都

喜欢打篮球的普通人 550

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

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

muduo网络库学习之EventLoop(一):事件循环类图简介和muduo 定时器TimeQueue

1、EventLoop、Channel、Poller 等类图如下: 黑色菱形:组合;白色菱形:聚合;白色三角形:继承;实线:关联; Channel是selectable IO channel,负责注册与响应IO 事件,它不拥有file descriptor。 Channel是Acceptor、Connector、EventLoop、TimerQueue、TcpConn

Meditation 1万+

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

netty的最新理解2019-3-29

昨天晚上突然明白了Netty的整体思路是怎么样的了。这个应该是作者的思路。 本身selector只能在一个线程内执行,这就导致了异步的执行了。所以,定义一个执行器,也就是EventLoop,来模拟一个线程池,当然,这个线程池比较特殊,只有一个线程的线程池,然后,再在这个线程池内部定义一个阻塞队列,这样就能实现跟线程池类似的功能了。借助select来遍历IO操作和任务队列,就能实现IO操作和...

weixin_30624825的博客 430

Java-66 深入浅出 分布式服务 Netty详解 EventLoop

Netty 是一个基于 Java NIO 的高性能、异步、事件驱动的网络通信框架,广泛用于构建分布式系统、RPC、即时通信和高并发服务端。它封装了复杂的 NIO 接口,采用 Reactor 线程模型,实现了异步非阻塞通信、零拷贝数据传输、内存池化管理等技术,显著提升系统吞吐和并发能力。Netty 的核心由 Channel、EventLoop、ChannelPipeline、ChannelHandler 等组件构成,支持 TCP、UDP、HTTP 等多种协议。

谢谢你的喜欢 我们一起无限进步 1012

29muduo_net库源码分析(五)

1.进程(线程)wait/notify (1)pipe (2)socketpair (3)eventfd,eventfd是一个比 pipe更高效的线程间事件通知机制,一方面它比 pipe 少用一个file descripor,节省了资源;另一方面,eventfd的缓冲区管理也简单得多,全部“buffer”只有定长8 bytes,不像 pipe 那样可能有不定长的真正buffer。 in

INGNIGHT的专栏 512

micro eventloop和macro eventloop

console.log('start') const interval = setInterval(() =&gt; { console.log('setInterval') }, 0) setTimeout(() =&gt; { console.log('setTimeout 1') Promise.resolve() .then(() =&gt; { ...

kangzai2012的博客 275

【2022最新Java面试宝典】—— Netty面试题(49道含答案)

目录1. Netty2. BIO3. IO 模式4. 五种 I/O 模型5. 阻塞 IO6. 非阻塞 IO7. IO 多路复用8. 信号驱动9. 异步 IO10.IO 多路复用11. select12. poll13. epoll14. Java的i/o15. Netty 线程模型和 Reactor 模式16. Reactor 单线程模型17. Reactor 多线程模型18. Reactor 主从线程模型19. Netty 使用 NIO 而不是 AIO20. Echo服务21. EventLoop和Eve

记录各种Bug解决案例及日常学习内容 4876

浅谈JS异步轮询和单线程机制

单线程特点执行异步操作   js是单线程语言,浏览器只分配给js一个主线程,用来执行任务(函数),但一次只能执行一个任务,这些任务就会排队形成一个任务队列排队等候执行.一般而已,相对耗时的操作是要通过异步来进行加载,可以避免导致页面的假死. setTimeout(function(){ console.log(这是timeout事件回调); },1000); ...

b18059735117的博客 151

java基础常问面试题,消息队列kafka面试

前言 这段时间也一直在学习Netty相关知识,因为涉及知识点比较多,也走了不少弯路。目前网上关于Netty学习资料玲琅满目,不知如何下手,其实大家都是一样的,学习方法和技巧都是总结出来的,我们在没有找到很好的方法之前不如按部就班先从基础开始,一般从总分总的渐进方式,既观森林,又见草木。 Netty是一款提供异步的、事件驱动的网络应用程序框架和工具,是基于NIO客户端、服务器端的编程框架。所以这里我们先以NIO和依赖相关的基础铺垫来进行剖析讲解,从而作为Netty学习之旅的一个开端。 流程描述 我们从bind

EDGNB123的博客 236

从零构建现代C++ Web服务器(一)

本文介绍了基于C++26的现代Web框架hical的设计理念与架构。文章首先分析了在2026年仍使用C++开发Web框架的合理性,指出在性能敏感、内存可控及C++生态集成的场景下具有独特优势。随后对比了现有C++ Web框架的优缺点,提出hical的设计目标:高性能、现代C++特性、可扩展性和开发友好性。框架采用核心层与适配层的分离架构,利用C++20协程、Concepts和C++26反射等特性实现零拷贝、自动路由注册等功能。文章还展示了事件循环接口的初步设计,体现了框架的抽象层次和内存管理理念。

Hical的博客 1251

Python中的异步io

#!/usr/bin/env python # -*- coding: utf-8 -*- # Created by TaoYuan on 2017/12/26 0026. # @Link : http://blog.youkuaiyun.com/lftaoyuan # Github : https://github.com/seeways # @Remark : Python学习群:3158574

TaoYuan 1548
上一篇: EpollPoller --- muduo_net库源码分析(27)
下一篇: EventLoopThread (30)
laohan_
博客等级 码龄15年 112粉丝 224原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值