前面几篇博客介绍了Epoll, ThreadPool,

其中 Epoll 封装了epoll的各类api, 可在epoll句柄中添加/修改/删除 fd 的 各类事件(EPOLLIN | EPOLLOUT), 可以返回一个 active的事件集合,并提供了一个回调函数指针,统一处理这些事件。

ThreadPool 则封装了一个任务队列,一个线程vector, 可以网线程池添加任务,并新建线程从任务队列取出任务执行。

这一篇将结合前面的Epoll和ThreadPool, 实现一个事件管理器,使用Epoll 添加和监听 fd 的 可读或可写事件,注册统一的事件处理接口来处理active的事件集合,将该回调函数放进任务队列,通知并等待线程池分配线程处理,统一的事件处理接口里则通过查询一个fd, callback的map, 找到对应fd的callback, 并加进任务队列,通知并等待线程池新建或者分配线程来处理。事件处理器每次添加或者修改fd的事件时,都会更新相应的fd, callback 的map, 以备后续回调时查询使用。

#include "Epoll.h"
#include "ThreadPool.h"
#include<iostream> class EventManager{
public:
EventManager( int thread_pool_size );
EventManager( const EventManager& ) = delete;
EventManager& operator=( const EventManager& ) = delete;
~EventManager(); void SetThreadPoolSize( size_t size ) ;
size_t Size(); void AddTask( Base::Closure* task );
int AddTaskWaitingReadable( int fd, Base::Closure* task );
int AddTaskWaitingWritable( int fd, Base::Closure* task );
int RemoveAwaitingTask( int fd );
int ModifyTaskWaitingStatus( int fd, int status, Base::Closure* task ); void Start();
void AwaitTermination();
void Stop(); void EpollAwakeHandler( const Epoll::ActiveEvents* ); private:
FixedThreadPool thread_pool_;
Epoll epoll_;
std::map<int, Base::Closure*> inactive_tasks_map_;
std::mutex mutex_;
}; EventManager::EventManager(int thread_pool_size) {
// Create at least 3 working threads. One of them is epolling thread.
if (thread_pool_size < 3) {
thread_pool_size = 3;
}
thread_pool_.SetPoolSize(thread_pool_size);
} EventManager::~EventManager() {
std::cout << "deleting event manager" << std::endl;
} void EventManager::SetThreadPoolSize(size_t size) {
std::unique_lock<std::mutex> lock(mutex_);
thread_pool_.SetPoolSize(size);
} size_t EventManager::Size() {
std::unique_lock<std::mutex> lock(mutex_);
return thread_pool_.Size();
} // 将StartPolling (epoll_wait)作为一个任务,单独一个线程执行
// 先监听得到待处理的fd和事件的集合,再调用事件统一处理接口EpollAwakeHandler来对监听到的fd及事件进行处理,其处理过程是通过查map,将fd对应的回调函数加入任务队列,等待线程池分配线程
// 进行处理。
void EventManager::Start() {
// Set awake callback for epoll.
epoll_.SetAwakeCallBack(new Epoll::EpollAwakeCallBack(
std::bind(&EventManager::EpollAwakeHandler, this, std::placeholders::_1))); // 用EventManager::EpollAwakeHandle函数来初始化Epoll里的回调函数,在执行Epoll::StartPolli // ng的时候,先epoll_wait监听得到fd和事件集合,再调用该回调函数处理发生的事件。
// First add epolling thread to work.
thread_pool_.AddTask(Base::NewCallBack(&Epoll::StartPolling, &epoll_)); //新建一个线程来执行任务, 把 StartPolling 放入任务队列,notify_one 一个线程来执行StartPolling 任务 // Start the internal thread poll.
thread_pool_.Start(); // 开启线程池
} // 将监听到的事件fd集合作为参数传入EventManager::EpollAwakeHandler, 遍历事件集合,在inactive_tasks_map_里根据fd关键字查找对应的回调函数,
//如果能找到,就将该回到函数加入到任务队列,之后会有某个线程来执行
// EventManager::EpollAwakeHandler 相当于是一个事件处理的统一接口,封装了基于线程池对不同事件的不同处理过程
void EventManager::EpollAwakeHandler(const Epoll::ActiveEvents* active_events) {
std::unique_lock<std::mutex> lock(mutex_);
for (auto i = 0; i < active_events->num(); i++) {
int fd = active_events->events()[i].data.fd;
if (inactive_tasks_map_.find(fd) != inactive_tasks_map_.end()) {
//std::cout << "find task on fd " << fd << std::endl;
thread_pool_.AddTask(inactive_tasks_map_[fd]);
}
}
} void EventManager::AddTask(Base::Closure* task) {
thread_pool_.AddTask(task);
} // 在epoll句柄中添加了一个需要监听可读事件的描述符,并更新了inactive_tasks_map_中的该fd对应的回调函数
int EventManager::AddTaskWaitingReadable(int fd, Base::Closure* task) {
std::unique_lock<std::mutex> lock(mutex_);
int ret = epoll_.AddMonitorReadableEvent(fd); // 监听该fd上是否有可读事件发生,且只监听一次,当这次监听完成后, 如果还需要继续监听这个fd, 需要再次加入
if (ret) { // 成功返回0, 失败返回-1
return ret;
}
if (inactive_tasks_map_.find(fd) != inactive_tasks_map_.end()) { // 如果能在inactive_tasks_map_里找到该fd对应的回调函数,就先删除掉,然后再为该fd添加新的回调函数
delete inactive_tasks_map_[fd];
}
inactive_tasks_map_[fd] = task;
return 0;
}
// 在epoll句柄中添加了一个需要监听可写事件的描述符,并更新了inactive_tasks_map_中的该fd对应的回调函数
int EventManager::AddTaskWaitingWritable(int fd, Base::Closure* task) {
std::unique_lock<std::mutex> lock(mutex_);
int ret = epoll_.AddMonitorWritableEvent(fd);
if (ret) {
return ret;
}
if (inactive_tasks_map_.find(fd) != inactive_tasks_map_.end()) {
delete inactive_tasks_map_[fd];
}
inactive_tasks_map_[fd] = task;
return 0;
} int EventManager::RemoveAwaitingTask(int fd) {
std::unique_lock<std::mutex> lock(mutex_);
int ret = epoll_.DeleteMonitoringEvent(fd); // 在epoll句柄中删除了该fd以及该fd要监听的可读事件
if (ret) {
return ret;
}
if (inactive_tasks_map_.find(fd) != inactive_tasks_map_.end()) { // 并在inactive_tasks_map_中删除掉该项key-value对
delete inactive_tasks_map_[fd];
inactive_tasks_map_.erase(inactive_tasks_map_.find(fd));
}
return 0;
}
// 修改该fd需要监听的事件,并更新inactive_tasks_map_中该fd对应的回调函数
int EventManager::ModifyTaskWaitingStatus(int fd, int status, Base::Closure* task) {
std::unique_lock<std::mutex> lock(mutex_);
int ret = epoll_.ModifyMonitorEvent(fd, status);
if (ret) {
return ret;
}
if (inactive_tasks_map_.find(fd) != inactive_tasks_map_.end()) {
delete inactive_tasks_map_[fd];
}
inactive_tasks_map_[fd] = task;
return 0;
} void EventManager::AwaitTermination() {
thread_pool_.AwaitTermination();
} void EventManager::Stop() {
thread_pool_.Stop();
} int main(){
EventManager em(4); return 0; }

一个基于线程池和epoll的IO事件管理器的更多相关文章

  1. JDFS:一款分布式文件管理实用程序第一篇(线程池、epoll、上传、下载)

    一 前言 截止目前,笔者在博客园上面已经发表了3篇关于网络下载的文章,这三篇博客实现了基于socket的http多线程远程断点下载实用程序.笔者打算在此基础上开发出一款分布式文件管理实用程序,截止目前 ...

  2. 【Java TCP/IP Socket】基于线程池的TCP服务器(含代码)

    了解线程池 在http://blog.csdn.net/ns_code/article/details/14105457(读书笔记一:TCP Socket)这篇博文中,服务器端采用的实现方式是:一个客 ...

  3. 浅谈线程池(中):独立线程池的作用及IO线程池

    原文地址:http://blog.zhaojie.me/2009/07/thread-pool-2-dedicate-pool-and-io-pool.html 在上一篇文章中,我们简单讨论了线程池的 ...

  4. 设计模式:基于线程池的并发Visitor模式

    1.前言 第二篇设计模式的文章我们谈谈Visitor模式. 当然,不是简单的列个的demo,我们以电商网站中的购物车功能为背景,使用线程池实现并发的Visitor模式,并聊聊其中的几个关键点. 一,基 ...

  5. 基于线程池的多并发Socket程序的实现

    Socket“服务器-客户端”模型的多线程并发实现效果的大体思路是:首先,在Server端建立“链接循环”,每一个链接都开启一个“线程”,使得每一个Client端都能通过已经建立好的线程来同时与Ser ...

  6. requests模块session处理cookie 与基于线程池的数据爬取

    引入 有些时候,我们在使用爬虫程序去爬取一些用户相关信息的数据(爬取张三“人人网”个人主页数据)时,如果使用之前requests模块常规操作时,往往达不到我们想要的目的,例如: #!/usr/bin/ ...

  7. Python网络爬虫之cookie处理、验证码识别、代理ip、基于线程池的数据爬去

    本文概要 session处理cookie proxies参数设置请求代理ip 基于线程池的数据爬取 引入 有些时候,我们在使用爬虫程序去爬取一些用户相关信息的数据(爬取张三“人人网”个人主页数据)时, ...

  8. 基于线程池的多线程售票demo2.0(原创)

    继上回基于线程池的多线程售票demo,具体链接: http://www.cnblogs.com/xifenglou/p/8807323.html以上算是单机版的实现,特别使用了redis 实现分布式锁 ...

  9. 基于线程池的多线程售票demo(原创)

    废话不多说,直接就开撸import org.springframework.util.StopWatch;import java.util.concurrent.*;/** * 基于线程池实现的多线程 ...

  10. 发一个可伸缩线程池大小的python线程池。已通过测试。

    发一个可伸缩线程池大小的线程池. 当任务不多时候,不开那么多线程,当任务多的时候开更多线程.当长时间没任务时候,将线程数量减小到一定数量. java的Threadpoolexcutor可以这样,py的 ...

随机推荐

  1. Object.create() 方浅析

    Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__. Object.create(proto[, propertiesObject]) 参数 pro ...

  2. Jupyter notebook upload无法加载本地ipynb文件

    可能是本地浏览器(IE)问题,修改默认浏览器.

  3. 2.mysql存储过程、存储函数与触发器

    -- 创建一个存储过程DELIMITER $CREATE PROCEDURE 存储过程名称(返回类型 参数名1 参数类型1, ....) [ ...... ]BEGIN -- 具体组成存储过程的SQL ...

  4. c语言动态库与静态库

    // show.h #ifndef __SHOW_H_ #define __SHOW_H_ #include <stdio.h> #include "math.h" v ...

  5. 关于之前ecplices没法正常导入使用fx包的笔记

    办法一:在java build path里的libraries里导入跟工作环境版本(或者ecplices的jdk版本)相配的jar包. 注:如果导入的包是要用fx的,可以不直接改整个程序的工作环境,可 ...

  6. shell脚本,shell语法和结构(以Cshell/TC shell为例)

    1.C shell/TC shell常用语法和结构 (1) shbang行: #!/bin/sh,通知内核使用哪种内核解释脚本; #!/bin/csh 或 #!/bin/tcsh (2) 注释: 以# ...

  7. 微信小程序-关闭某个页面分享

    方式一: wx.hideShareMenu();    方式二:   

  8. YOLO v6:一个硬件友好的目标检测算法

    ​ 本文来自公众号"AI大道理"   YOLOv6 是美团视觉智能部研发的一款目标检测框架,致力于工业应用. YOLOv6支持模型训练.推理及多平台部署等全链条的工业应用需求,并在 ...

  9. 如何理解JavaScript中常用的4种排序算法?

      如何理解JavaScript中常用的4种排序算法? 冒泡排序 冒泡排序是我们在编程算法中,算是比较常用的排序算法之一,在学习阶段,也是最需要接触理解的算法,所以我们放在第一个来学习. 算法介绍: ...

  10. Java 接口内容小结

    Java接口学习:https://www.cnblogs.com/mlllily/p/14923837.html 小结内容: 在Java9+版本中,接口内容可以有常量.抽象方法.默认方法.静态方法.私 ...