深入浅出Reactor和Proactor模式

简介: 深入浅出Reactor和Proactor模式

       Reactor模式和Proactor模式是两种常见的设计模式,用于处理事件驱动的并发编程。它们在处理IO操作时有着不同的工作方式和特点。


对于到来的IO事件(或是其他的信号/定时事件),又有两种事件处理模式:


  • Reactor模式:要求主线程(I/O处理单元)只负责监听文件描述符上是否有事件发生(可读、可写),若有,则立即通知工作线程,将socket可读可写事件放入请求队列,读写数据、接受新连接及处理客户请求均在工作线程中完成。(需要区别读和写事件)
  • Proactor模式:主线程和内核负责处理读写数据、接受新连接等I/O操作,工作线程仅负责业务逻辑(给予相应的返回url),如处理客户请求。

Reactor模式:

工作方式:Reactor模式使用一个事件循环/事件轮询器(通常是主线程),负责监听和分发IO事件。当有IO事件发生时,Reactor会调用相应的处理函数进行处理。

优点:

  • 简单且易于理解和实现。通常只需要一个事件循环器以及事件处理函数即可实现。
  • 可以处理大量的并发连接,因为使用一个线程来处理所有的IO事件,减少了线程切换的开销

缺点:

  • 对于处理耗时的IO操作阻塞,会导致整个Reactor的处理能力下降,无法充分利用CPU资源。
  • 无法利用多核处理器的优势,因为只有一个线程用于处理IO事件。

具体框架:

class Reactor {
public:
    // 构造函数,初始化事件循环
    Reactor();
 
    // 注册事件处理器
    void registerEventHandler(EventHandler* handler, EventType type);
 
    // 事件循环
    void eventLoop();
 
private:
    // 事件处理器映射表
    std::map<EventType, EventHandler*> eventHandlers;
};
 
// 事件处理器接口
class EventHandler {
public:
    virtual void handleEvent() = 0;
};
 
// 具体的事件处理器
class ConcreteEventHandler : public EventHandler {
public:
    virtual void handleEvent() override {
        // 处理事件的具体逻辑
    }
};

Proactor模式:

工作方式:Proactor模式使用多线程来处理IO事件。一个线程负责发起IO操作,当操作完成后,另一个线程会收到通知并处理完成的IO操作。

(图借鉴网络)

优点:

  • 可以充分利用多核处理器的优势来处理并发IO操作。
  • 每个线程可以同时处理多个IO事件。
  • 高效利用CPU资源,即使有部分IO操作阻塞也不会影响其他线程的正常工作。

缺点:

  • 实现较复杂,涉及多线程之间的同步和通信,需要额外的开销。
  • 每个IO操作都需要启动两个线程,可能会导致系统开销较大。

具体框架:

class Proactor {
public:
    // 构造函数,初始化异步操作队列
    Proactor();
 
    // 异步读操作
    void asyncRead(File* file, CompletionHandler* handler);
 
    // 异步写操作
    void asyncWrite(File* file, const std::string& data, CompletionHandler* handler);
 
    // 事件循环
    void eventLoop();
 
private:
    // 异步操作队列
    std::queue<AsyncOperation*> asyncOperations;
};
 
// 异步操作接口
class AsyncOperation {
public:
    virtual void execute() = 0;
};
 
// 异步读操作具体实现
class AsyncReadOperation : public AsyncOperation {
public:
    virtual void execute() override {
        // 执行异步读操作
    }
};
 
// 异步写操作具体实现
class AsyncWriteOperation : public AsyncOperation {
public:
    virtual void execute() override {
        // 执行异步写操作
    }
};
 
// 完成处理器接口
class CompletionHandler {
public:
    virtual void handleCompletion() = 0;
};
 
// 具体的完成处理器
class ConcreteCompletionHandler : public CompletionHandler {
public:
    virtual void handleCompletion() override {
        // 处理完成事件的具体逻辑
    }
};
相关文章
|
存储 XML 网络协议
|
网络协议 安全 网络安全
【UDP】——为什么 UDP 数据包不能超过 512 个字节
一开始了解的是 DNS 服务使用的是 UDP 协议,后面看到 DNS 服务主要使用 UDP 协议,在少数情况(传输的数据超过 512 个字节)下也会使用 TCP 协议,因为 UDP 数据包不能超过 512 个字节。那问题来了,为什么 UDP 数据包不能超过 512 个字节呢?
5553 0
【UDP】——为什么 UDP 数据包不能超过 512 个字节
|
11月前
|
SQL 存储 关系型数据库
MySQL中到底什么是覆盖索引、索引下推?
覆盖索引指查询只需通过索引即可获取数据,无需回表,提升查询效率。索引下推则在索引遍历时提前过滤条件,减少回表次数,尤其适用于联合索引中部分字段无法使用的情况,二者均能显著降低I/O开销,提高查询性能。(238字)
919 1
|
编解码 缓存 Ubuntu
在Ubuntu平台搭建RTMP直播服务器使用SRS简要指南
这个简要指南提供了搭建基本 RTMP 直播服务所需知道信息. SRS 还提供许多高级特性如 DVR、转码、边缘缓存等. 若要利用这些特性, 需要进一步阅读官方文档并相应地调整配置文件.
1087 0
|
人工智能 前端开发 架构师
Agent 2.0“三剑客”:MCP协议、A2A协议、AG-UI协议
Agent 2.0“三剑客”:MCP协议、A2A协议、AG-UI协议
|
存储 缓存 算法
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
本文介绍了多线程环境下的几个关键概念,包括时间片、超线程、上下文切换及其影响因素,以及线程调度的两种方式——抢占式调度和协同式调度。文章还讨论了减少上下文切换次数以提高多线程程序效率的方法,如无锁并发编程、使用CAS算法等,并提出了合理的线程数量配置策略,以平衡CPU利用率和线程切换开销。
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
|
存储 网络协议 安全
Epoll的实现原理
Epoll的实现原理
|
数据库 C++
【数据结构进阶】红黑树超详解 + 实现(附源码)
本文深入探讨了红黑树的实现原理与特性。红黑树是一种自平衡二叉搜索树,通过节点着色(红/黑)和特定规则,确保树的高度接近平衡,从而实现高效的插入、删除和查找操作。相比AVL树,红黑树允许一定程度的不平衡,减少了旋转调整次数,提升了动态操作性能。文章详细解析了红黑树的性质、插入时的平衡调整(变色与旋转)、查找逻辑以及合法性检查,并提供了完整的C++代码实现。红黑树在操作系统和数据库中广泛应用,其设计兼顾效率与复杂性的平衡。
4389 3
|
设计模式 算法 网络协议
社招offer-腾讯T9-70W年薪(面试经验分享)(上)
社招offer-腾讯T9-70W年薪(面试经验分享)
|
关系型数据库 MySQL
mysql查看当前实时连接数最大连接数
mysql查看当前实时连接数最大连接数
3268 0