muduo的reactor模式基本实现

muduo中的reactor模式实现浅析 muduo中的reactor模式实现浅析 最近刚刚看完了陈硕大神的《Linux多线程服务端编程》,对muduo源码也略有阅读,故作此总结。 Reactor模型 先来看看定义: The reactor design pattern is an event handling pattern for handling service requests delivered concurrently to a service handler by one or more inputs. The service ha 阅读详情

这几天一直在看muduo的Eventloop(事件循环)这一块的源代码,感觉里面有好多东西例如:智能指针的使用,将eventfd,timerfd等linux新性能运用进去,C++一些容器的合理使用,还有如何能在多线程情况下减少锁的使用等都是我们应该学习的东西。

关于muduo实现的reactor模式,有三个关键的类

.事件分发器类Channel
.封装了I/O复用的Poller
.定时器接口类TimerQueue

接下来就给大家先一一介绍这几个类,然后才给大家介绍reactor的基本构成

1.事件分发类Channel

事件分发器Channel的数据成员如下

//定义事件类型变量
            static const int kNoneEvent;    //无事件
            static const int kReadEvent;    //可读事件
            static const int kWriteEvent;   //可写事件

            EventLoop *loop_;               //channel所属的loop
            const int fd_;                  //channel负责的文件描述符
            int events_;            //注册的事件
            int revents_;           //就绪的事件
            int index_;             //被poller使用的下标
            bool logHup_;           //是否生成某些日志

            boost::weak_ptr<void> tie_; //
            bool tied_;
            bool eventHandling_;
            bool addedToLoop_;
            ReadEventCallback readCallback_;    //读事件回调
            EventCallback writeCallback_;       //写事件回调
            EventCallback closeCallback_;       //关闭事件回调
            ReadEventCallback errorCallback_;   //错误事件回调

其中EventCallback和ReadEventCallback的声明如下

 typedef boost::function<void()> EventCallback;
            typedef boost::function<void(Timestamp)> ReadEventCallback;
        //处理事件
        void handleEvent(Timestamp receiveTime);

        //设置可读事件回调
        void setReadCallback(const ReadEventCallback &cb)
        {
            readCallback_ = cb;
        }

        //设置可写事件回调
        void setWriteCallback(const EventCallback &cb)
        {
            writeCallback_ = cb;
        }

        //设置关闭事件回调
        void setCloseCallback(const EventCallback &cb)
        {
            closeCallback_ = cb;
        }

        //设置错误事件回调
        void setErrorCallback(const EventCallback &cb)
        {
            errorCallback_ = cb;
        }

        void tie(const boost::shared_ptr<void>&);



        //返回注册的事件
        int events()const
        {
            return events_;
        }

        //设置注册事件
        void set_revents(int revt)
        {
            revents_ = revt;
        }

        //判断是否注册的事件
        bool isNoneEvent()const
        {
            return events_ == kNoneEvent;
        }

        //设置可读事件
        void enableReading()
        {
            events_ |= kReadEvent;
            update();
        }

        //销毁读事件
        void disableReading()
        {
            events_ &= ~kReadEvent;
            update();
        }

        //注册写事件
        void enableWriting()
        {
            events_ |= kWriteEvent;
         update();   
        }

        //销毁写事件
        void disableWriting()
        {
            events_ &= ~kWriteEvent;
            update();
        }

        //销毁所有事件
        void disableAll()
        {
            events_ = kNoneEvent;
            update();
        }

        //是否注册可写事件
        isWriting()const
        {
            return events_ & kWriteEvent;
        }
这里写代码片

Channel的主要功能为管理各种注册给poller的套接字描述符及其上发生的事件,以及事件发生了所调的回调函数

Channel的主要作用如下

1.首先我们给定Channel所属的loop以及其要处理的fd
2.接着我们开始注册fd上需要监听的事件,如果是常用事件(读写等)的话,我们可以直接调用接口enable***来注册对应fd上的事件,与之对应的是disable*用来销毁特定的事件
3.在然后我们通过set***Callback来事件发生时的回调

2.I/O复用类Poller

Poller类是个基类,它的定义如下

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

            Poller(EventLoop *loop);

            virtual ~Poller();

            //不许在I/O线程中调用,I/O复用的封装
            virtual Timestamp poll(int timeoutMs,ChannelList *activeChannels) = 0;

            //跟新Channel
            virtual void updateChannel(Channel *channel) = 0;

            //移除Channel
            virtual void removeChannel(Channel *channel) = 0;

            //这个channel是否在map中存在
            virtual bool hasChannel(Channel *channel)const;

            //默认poller方式
            static Poller *newDefaultPoller(EventLoop *loop);

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

        protected:
            typedef std::map<int,Channel*> ChannelMap;
            ChannelMap Channels_;           //存储事件分发器的map
        private:
            EventLoop *owerLoop_;           //属于哪个loop
    };

需要注意的是我们的事件分发器channel集用关联容器map来保存,map的关键字为channel所管理的fd,这样我们在更新已有的channel时时间复杂多会将为O(1)

接下来我们谈谈epoll对Poller的实现
EPollPoller类的定义为

class EPollPoller : public Poller
{
    public:
        EPollPoller(EventLoop *loop);
        virtual Timestamp poll(int timeoutMs,ChannelList *activeChannels);   //内部调用epoll_wait函数

        virtual void updateChannel(Channel *channel);

        virtual void removeChannel(Channel *Channel);       
    private:
        static const int kInitEventListSize = 16;           //epoll事件表的大小

        static const char *operatoionToString(int op);

        void fillActiveChannels(int numEvents,ChannelList *activeChannels)const; //将epoll返回的活跃事件填充到activeChannel

        void update(int operation,Channel *channel);    //对Channel的更改操作

        typedef std::vector<struct epoll_event> EventList;

        int epollfd_;       //epoll的事件表fd

        EventList events_;  //epoll事件数组
};

Poller类的主要功能如下

.调用poll函数监听注册了事件的文件描述符
.当poll返回时将发生事件的事件集装入channel中
.可以控制channel中事件的增删改

3.定时器TimerQueue

EventLoop直接调用的是定时器队列类TimerQueue类,该类的数据定义如下

typedef std::pair<Timestamp,Timer *> Entry;     //定时器入口
            typedef sta::set<Entry> TimerList;              //定时器集合
            typedef std::pair<Timer *,int64_t> ActiveTimer; //到时的定时器
            typedef std::set<ActiveTimeri> ActiveTimerSet;  //活跃的定时器集合

EventLoop *loop_;       //定时器所在的I/O循环
            const int timerfd_;     //定时器描述符
            Channel timerfdChannel_;

            TimerList timers_;      //定时器集合

            //for cancel()
            ActiveTimerSet activeTimers_;
            bool callingExpiredTimers_;
            ActiveTimerSet cancelingTimers_;

由上图我们发现我们用来存储定时器集的容器使用了set,set针对定时器有它天然的优势,首先set的特性是所存储的元素为默认升序的,这样当我们某个事件点取到期的定时器,就直接取该时间点之前的所有定时器就好,其次我们往set中添加定时器的效率也相对较好为0(logn)。但是用set有个问题,我们如何存储俩个定时时间一样的定时器呢?muduo的解决方案就是使用一个pair类型,pair为pair

增加一个定时器
 TimerId addTimer(const TimerCallback &cb,
                            Timestamp when,
                            double interval);

删除一个定时器
            void cancel(TimerId timerId);

上述接口函数,都不是线程安全的,但是muduo中并没有用加锁来解决问题,而是通过EventLoop中runInloop函数来使,上述函数在主I/O线程中执行,这样做的好处是,我们可以在上述函数实现中不使用锁,程序在运行过程中就会减少由于锁而引起的上下文切换,由于主I/O线程也不一定是一直忙,所以这种做法可能会在效率上有所提升

4.EventLoop类的实现

EventLoop类实现了reactor的基本模式
它的数据定义如下

typedef std::vector<Channel *> ChannelList; //事件分发器列表

            bool looping_;                  //是否在运行
            bool quit_;                     //是否退出事件循环
            bool eventHandling_;
            bool callingPendingFunctors_;
            int64_t iteration_;             //事件循环的次数
            const pid_t threadId_;          //运行loop的线程ID
            Timestamp pollReturnTime_;      //poll阻塞的时间
            boost::scoped_ptr<Poller> poller_;  //IO复用
            boost::scoped_ptr<TimerQueue> TimerQueue_;//定时器队列
            int wakeupFd_;                            //唤醒套接字
            boost::scoped_ptr<Channel> wakeupChannel_; //封装事件描述符 
            boost::any context_;

            ChannelList activeChannel_;             //以活跃的事件集
            Channel *currentActiveChannel_;         //当前处理的事件集

            MutexLock mutex_;                       //封装的互斥锁
            std::vector<Functor> pendingFunctors_;  //需要在主I/O线程执行的任务

EventLoop通过boost库下的智能指针scoped_ptr来管理Poller_,TimerQueue_,wakeupChannel_对象,这样不容易发生内存显露,其中变量pendingFunctors_为需要在I/O线程中执行的任务集,例如上面所讲的定时器的增删接口的执行,就会先放在此集合里,然后有主I/O线程来执行,那么主线程在调用loop函数之后会阻塞在poller函数中,此时我们应该如何唤醒I/O线程呢?muduo中采用了linux的新特性eventfd来唤醒I/O线程,他的具体用法在我的前几篇博客中有介绍

Eventloop的主要功能如下

.首先我们因该调用updateChannel来添加一些事件
.接着我们就可以调用loop函数来执行事件循环了,在执行事件循环的过程中,我们会阻塞在poller_poll调用处,当有事件发生时,Poller类就会把活跃的事件放在activeChannel集合中
.之后我们调用Channel中的handleEvent来处理事件发生时对应的回调函数,处理完事件函数后还会处理必须有I/O线程来完成的doPendingFuncors函数

当然我们可以在中间的过程中注册一些普通事件或通过run*类函数来注册定时事件,我们也可以调用updateChannel和removeChannel来增删该Channel

EventLoop的源码如下

EventLoop.h

#ifndef MUDUO_NET_EVENTLOOP_H
#define MUDUO_NET_EVENTLOOP_H

#include <vector>

#include <boost/any.hpp>
#include <boost/function.hpp>
#include <boost/noncopyable.hpp>
#include <boost/scoped_ptr.hpp>

#include <muduo/base/Mutex.h>
#include <muduo/base/CurrentThread.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;

    class EventLoop : boost::noncopyable
    {
        public:
            typedef boost::function<void()> Functor;

            EventLoop();
            ~EventLoop();

            //主循环
            void loop();

            //退出主循环
            void quit();

            //poll延迟的时间
            Timestamp pollReturnTime()const
            {
                return pollReturnTime_;
            }

            //迭代次数
            int64_t iteration()const
            {
                return iteration_;
            }

            //在主循环中运行
            void runInLoop(const Functor &cb);

            //插入主循环任务队列
            void queueInLoop(const Functor &cb);

            //某个时间点执行定时回调
            TimerId runAt(const Timestamp &time,const TimerCallback &cb);

            //某个时间点之后执行定时回调
            TimerId runAfter(double delay,const TimerCallback & cb);

            //在每个时间间隔处理某个回调事件
            TimerId runEvery(double interval,const TimerCallback &cb);

            //删除某个定时器
            void cancel(TimerId timerId);

            //唤醒事件通知描述符
            void wakeup();

            //跟新某个事件分发器
            void updateChannel(Channel *channel);

            //移除某个事件分发器
            void removeChannel(Channel *channel);

            bool hasChannel(Channel *channel);

            //如果不在I/O线程中则退出程序
            void assertInLoopThread()
            {
                if(!isInLoopThread())
                {
                    abortNotInLoopThread();
                }
            }

           // 检测是否在I/O线程中
            bool isInLoopThread()const
            {
                return threadId_ == CurrentThread::tid();
            }

            //是否正在处理事件
            bool eventHandling()const { return eventHandling_;}

            void setContext(const boost::any &contex)
            {
                return context_;
            }

            const boost::any &getContext()const
            {
                return context_;
            }

            boost::any *getMutableContext()
            {
                return &context_;
            }

            //判断当前线程是否为I/O线程
            static EventLoop *getEventLoopOfCurrentThread();

        private:
            //不在主I/O线程
            void abortNotInLoopThread();

            //将事件通知描述符里的内容读走,以便让其继续检测事件通知
            void handleRead();

            //执行转交给I/O的任务
            void doPendingFunctors();

            //将发生的事件写入日志
            void printActiveChannels()const;

            typedef std::vector<Channel *> ChannelList; //事件分发器列表

            bool looping_;                  //是否在运行
            bool quit_;                     //是否退出事件循环
            bool eventHandling_;
            bool callingPendingFunctors_;
            int64_t iteration_;             //事件循环的次数
            const pid_t threadId_;          //运行loop的线程ID
            Timestamp pollReturnTime_;      //poll阻塞的时间
            boost::scoped_ptr<Poller> poller_;  //IO复用
            boost::scoped_ptr<TimerQueue> TimerQueue_;//定时器队列
            int wakeupFd_;                            //唤醒套接字
            boost::scoped_ptr<Channel> wakeupChannel_; //封装事件描述符 
            boost::any context_;

            ChannelList activeChannel_;             //以活跃的事件集
            Channel *currentActiveChannel_;         //当前处理的事件集

            MutexLock mutex_;                       //封装的互斥锁
            std::vector<Functor> pendingFunctors_;  //需要在主I/O线程执行的任务

    };
}
}
#endif MUDUO_NET_ENENTLOOP_H

EventLoop.cc

#include <muduo/net/EventLoop.h>

#include <muduo/base/Logging.h>
#include <muduo/base/Mutex.h>
#include <muduo/net/Channel.h>
#include <muduo/net/Poller.h>
#include <muduo/net/SocketsOps.h>
#include <muduo/net/TimerQueue.h>

#include <boost/bind.hpp>

#include <signal.h>
#include <sys/eventfd.h>

using namespace muduo;
using namespace muduo::net;

namespace
{
    __thread EventLoop *t_loopInThisThread = 0;

    const int kPollTimeMs = 10000;

    int createEventfd()
    {
        int evtfd = ::eventfd(0,EFD_NONBLOCK | EFD_CFD_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),
    iteration_(0),
    threadId_(CurrentThread::tid()),
    poller_(Poller::newDefaultPoller(this)),
    timerQueue_(new TimerQueue(this)),
    wakeupFd_(createEventfd()),
    wakeupChannel_(new Channel(this,wakeupFd_)),
    currentActiveChannel_(NULL)
{
    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(boost::bind(&EventLoop::handleRead,this));

    //注册可读事件
    wakeupChannel_->enableReading();
}


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

void EventLoop::loop()
{
    assert(!looping_);
    assertInLoopThread();
    looping_ = true;
    quit_ = false;

    LOG_TRACE << "EventLoop" << this << "start looping";

    while(!quit_)
    {
        activeChannels_.clear();
        pollReturnTime_ = poller_->poll(kPollTimeMs,&activeChannels_);
        //计算执行多少次I/O返回
        if(Logger::LogLevel() <= Logger::TRACE)
        {
            printActiveChannels();
        }

        eventHandling_ = true;

        //处理就绪事件
        for(ChannelList::iterator it = activeChannels_.begin();
                it != activeChannels_.end(); ++it)
        {
            currentActiveChannel_ = *it;
            currentActiveChannel_->handleEvent(pollReturnTime_);
        }

        currentActiveChannel_ = NULL;
        eventHandling_ = false;
        //处理一些其他任务
        doPendingFunctors();
    }

    LOG_TRACE << "EventLoop" << this << "stop looping";
    looping_ = false;
}

void EventLoop::quit()
{
    quit_ = true;

    if(!isInLoopThread())
    {
        wakeup();
    }
}

void EventLoop::runInLoop(const Functor &cb)
{
    if(isInLoopThread())
    {
        cb();
    }
    else
    {
        queueInLoop(cb);
    }
}


void EventLoop::queueInLoop(const Functor &cb)
{
    {
        MutexLockGuard lock(mutex_);
        pendingFunctors_.push_back(cb);
    }

    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);
}

bool EventLoop::hasChannel(Channel *channel)
{
    assert(channel->ownerLoop() == this);
    assertInLoopThread();
    return poller_->hasChannel(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);
    if(n != sizeof one)
    {
        LOG_ERROR << "EventLoop::wakeup() writes" << n << "bytes instead of 8";
    }
}

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";
    }
}

void EventLoop::doPendingFunctors()
{
    std::vector<Functor> Functors;
    callingPendingFunctors_ = true;

    {
        MutexLockGuard lock(mutex_);
        Functors.swap(pendingFunctors_);    //提高效率且防止死锁
    }

    for(ssize_t i = 0;i < Functors.size();++i)
    {
        Functors[i]();
    }
    callingPendingFunctors_ = false;
}
从epoll构建muduo-3 加入第一个类,顺便介绍reactor mini-muduo v 0.02版本,这个版本添加的内容非常少,完整可运行的示例可从github下载,使用命令git checkout v0.02可切换到此版本,在线浏览到这里 #include "TcpServer.h" int main(int args, char** argv) { TcpServer tcpserver; tcpserver.start(); 阅读详情

相关推荐

muduo---C++网络编程库

陈硕 muduo 参考资料 muduo源码剖析 muduo基于Reactor模式实现 Reactor模式也是目前大多数Linux端高性能网络编程框架和网络应用所选择的主要架构,例如内存数据库Redis和Java的Netty库等。 muduo 架构 整个架构依照reactor模式,如如下图: 所谓的reactor模型,是有一个循环的过程,监听对应事件是否触发,触发时调用对应的callback函数进行处理; 这里的事件在muduo中包括socket可读写事件、定时器事件。在其他网络库中如libevent也包

weixin_48433164的博客 3941

muduo库的模拟实现——Reactor部分

本文介绍了muduo库模拟实现的Reactor部分

写博客记录分享学习笔记 1660

muduo 架构解析

muduo是一个基于Reactor模式的C++网络库。它采用非阻塞I/O模型,基于事件驱动和回调。它不仅是学习linux服务端多线程网络编程的入门资料,我们也可以通过它来学习C++11。     Reactor是网络编程的一般范式。我们这里从reactor模式为出发点,根据Reactor模式的特点剖析muduo的架构设计。根据wiki的定义: The reactor design patter

Swartz2015的专栏 9135

基于Reactor模型的高性能网络库

EventLoop在epoll_create,注册各个Channel之后,处于epoll_wait阻塞状态,要想唤醒当前的EventLoop去执行新的连接,通过往wakefd上写入一个字符,唤醒当前的EventLoop。在多Reactor-多线程模型中,运用one loop per thread的思想,由一个mainReactor负责accept连接,然后把该连接挂载到某个subReactor,多个连接分配到多个线程,充分利用CPU。接收新用户连接后,通过轮询来选择subReactor并给它分发连接。

m0_75034791的博客 1718

muduo库reactor类结构分析

用来分析muduo的reactor结构,怕自己忘了,写个思维导图总结一下

(一)Tushare Pro教程:股票基本数据接口

 一直使用tushare作数据源,这里开个教程大家一起学习。 一、介绍        tushare是一个python股票数据源,现已升级到tushare pro版本,新版本在数据稳定性和获取速率上大幅提升。与wind、东财等收费数据源相比,tushare pro算是金融数据界的良心之作,其为从事金融工程、量化研究等方向的同学们提供了便利。tushare pro还有一点突出的特色就是已经将股票...

虎毛的博客 4万+

C++基础:Reactor模型设计思想与muduo架构理解

本文总结了作者学习muduo网络库的阶段性成果。首先介绍了Reactor模式的特点,指出其采用同步非阻塞的IO多路复用机制,通过eventLoop高效管理多个文件描述符。然后解读了muduo库的核心架构,分析了TcpServer、EventLoop、Channel等组件的协作关系。最后结合EchoServer实例,详细说明了muduo处理连接建立和数据读写的完整流程,包括任务派发、事件回调等关键环节。文章展现了muduo如何通过Reactor模式实现高性能网络服务

SashiMoore的博客 653

程序自动化填写网页表单数据

如何让程序自动化填写网页表单数据,特别是涉及到批量数据情况时,可以减少人力。下面是涉及到的一些场景,都可以通过相关自动化程序实现。

Y 2424

muduo网络库:Reactor模型的介绍

首先我们先了解几个问题: 多线程能提高并发度吗? 如果指的是“并发连接数”,那么不能。 假如单纯采用 thread per connection 的模型,对于32位 linux,一个进程的地址空间是4G,其中用户态能访问3G左右,而一个线程的默认栈大小为10M,心算可以得到,一个进程大约最多能同时启动300个线程左右,这远远低于基于事件单线程程序所能轻松达到的并发连接数(几千上万,甚至几万)。所谓...

月光骆驼的博客 1389

硬盘dma读写过程

保护模式中,pci上的硬盘dma读写方法

480

基于Ardupilot/PX4固件,APM/PIXhawk硬件的VTOL垂直起降固定翼软硬件参数调试(第二篇)软硬件参数调试

基于Ardupilot/PX4固件,APM/PIXhawk硬件的VTOL垂直起降固定翼软硬件参数调试(第二篇) 本文内容大部分来自Kris,我们的K大,在VTOL领域的大牛,在此,非常感谢K大。能引用K大的成果也是自己的荣幸,不便之处还请K大多多包涵,在此引用只是为方便自己日常学习,部分参数表里根据自身情况,增加了修改环节和相应参数。 垂直起降固定翼 中文教程-参数设置 垂直起降固定翼是由 多轴+...

u011322358的博客 2万+

Muduo - Reactor模式

Muduo - Reactor模式 一、Reactor 是什么 wiki的中文定义:Reactor模式是事件驱动的,有一个或多个并发输入源,有一个Service Handler,有多个Request Handler,这个Service Handler会同步的将输入的请求(Event)多路复用的分发给相应的Request Handler。 从上述文字中我们可以看出以下关键点: 事件驱动(event handling) 可以处理一个或多个输入源(one or more inputs) 通过Servic

mohuak的博客 690

C#串口通信革命:RJCP.DLL.SerialPortStream实战全解析

本文介绍了如何通过RJCP.DLL.SerialPortStream库实现高性能串口通信解决方案。相比.NET自带的SerialPort类,该库提供了完全的异步非阻塞IO、动态缓冲区控制和多线程事件驱动等优势,特别适合工业自动化和物联网场景。文章详细讲解了环境搭建、核心功能对比,并通过代码示例展示了端口配置、异步通信管道搭建等关键技术。最后以Modbus RTU协议为例,演示了高级协议封装方法,为开发者提供了完整的工业级串口通信实现方案。

墨夶的博客 1868

muduo网络库学习笔记(9):Reactor模式的关键结构

muduo网络库-Reactor模式的关键结构

li27z的博客 3191

踩过20+工控视觉项目的坑后,我用C# + 海康相机 + YOLOv11实现了零依赖产线缺陷检测

触发式检测:只有光电传感器检测到零件到位,才触发相机拍照和算法检测,不是持续推理,减少工控机性能占用;全链路本地运行:从图像采集、算法推理到不良品剔除,所有逻辑全在本地工控机运行,不需要外网、不需要云端服务,哪怕产线断网也能正常运行;异常降级机制:算法推理异常时,系统自动触发产线缓停,同时声光告警,不会让不良品流到下工序,满足汽车行业IATF16949质量要求;模块化解耦:算法核心层和业务逻辑层完全分离,后续更换YOLO模型、新增缺陷类型,只需要替换模型文件,不需要修改业务逻辑;全链路可追溯。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 152

Qt 国产嵌入式操作系统实现文字转语音功能(ekho库)

Qt 国产嵌入式操作系统实现文字转语音功能(TTS)_tts驱动 转文字-优快云博客在继上篇文章之后,甲方粑粑不满意使用eSpeak发出的声音太难听了,这就不得不找个替代品,声音稍微好听了一些。使用ekho开源库。Ekho(余音)是一个免费、开源的中文语音合成软件。它目前支持粤语、普通话(国语)、诏安客语、藏语、雅言(中国古代通用语)和韩语(试验中),英文则通过Festival间接实现。Ekho支持Linux、Windows和Android平台。

希望能帮助大家,希望大家多多支持,你们的支持是我前进的动力。 2753

muduo库源码剖析(一) reactor模式

一. Reactor模式简介 Reactor释义“反应堆”,是一种事件驱动机制。和普通函数调用的不同之处在于:应用程序不是主动的调用某个API完成处理,而是恰恰相反,Reactor逆置了事件处理流程,应用程序需要提供相应的接口并注册到Reactor上,如果相应的时间发生,Reactor将主动调用应用程序注册的接口,这些接口又称为“回调函数”。 二. moduo库Reactor模式的实现 ...

weixin_30882895的博客 113

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

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

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

m0_73537205的博客 2032

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

发布一个基于 Reactor 模式的 C++ 网络库

Muduo 是我在业余时间编写的基于 Reactor 模式的 C++ 网络库,适用于 Linux 平台,支持多线程。本文主要介绍 muduo 网络库的使用。其设计与实现将有另文讲解。

陈硕的Blog 9万+

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

基于主从模式的Reactor的仿muduo网络库

基于Reactor的仿muduo网络库

VHhhbb的博客 2224

muduo库的模拟实现——muduo库的介绍

本文是muduo库模拟实现项目记录的第一篇文章,简单介绍了一下muduo库的背景知识以及基本结构。

写博客记录分享学习笔记 1894

muduo网络库之Reactor模式

muduo网络库中的Reactor模式

lucas1018的博客 490

muduo源码学习(一):Reactor模式与muduo

Reactor模式 reactor模式一共有5种角色构成: Handle(句柄或者描述符) : 本质上是一种资源,由操作系统提供。因为再windows中我们叫句柄handle,在linux中我们可以认为就是描述符。作为一个事件代号。举例说,网络编程中的socket描述符。事件既可以来自于外部(如:客户端的连接请求,客户端的发送的数据可读事件),也可以来自于内部(操作系统产生的定时器事件等)。它本...

qq_22121229的博客 299

主从reactor模式

muduo中,类EventLoopThreadPool的构造函数将成员numThreads_设置为0,表示默认不开启主从reactor模式。 通过在调用void TcpServer::start()之前,调用EventLoopThreadPool::setThreadNum()。即可开启主从reactor模式。 类TcpServer的结构如下: class TcpServer : noncopyable { public: ... private: EventLoop* loop_; // the a

xupeng1644的博客 1954

[muduo] Buffer缓冲区 | TcpServer | Reactor模式

Reactor 模式 = 1 个前台 + N 个专项服务员 → 资源利用率最大化 核心思想:又加了一层,实现了内聚和各司其职

No Game No Life 1114
上一篇: linux新增特性eventfd
下一篇: 浅析linux服务端socket编程
Shreck66
博客等级 码龄12年 119粉丝 91原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值