前面几篇博客介绍了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. 注释:MARK与TODO、FIXME

    MARK: 在OC中的用法: #pragma mark -说明文字(可以不加-) 在swift中的用法:// MARK: - 说明文字(可以不加-) TODO.FIXME(不区分OC.swift) / ...

  2. Neural Network模型复杂度之Residual Block - Python实现

    背景介绍 Neural Network之模型复杂度主要取决于优化参数个数与参数变化范围. 优化参数个数可手动调节, 参数变化范围可通过正则化技术加以限制. 本文从优化参数个数出发, 以Residual ...

  3. (0724) 格雷码 verilog

    https://blog.csdn.net/gordon_77/article/details/79489548 assign gray_value = (binary_value >> ...

  4. 无锡哲讯谈食品行业如何利用SAP信息化方案实现数字化转型?

    随着人们对生活品质的提高,大家对食品安全问题越来越重视.食品企业如果缺乏相应的监管和追溯,很容易陷入困难的被动局面.SAP系统可以对食品加工企业供应链.生产销售.食品质量控制等环节的信息化管控,降低食 ...

  5. Django操作mongo数据库一(配置文件里写连接信息)

    一.基本环境 1.开发环境: Python环境:Python 3.8.16 Django环境:4.1 2.需要安装的包 pip install pymongo pip install mongoeng ...

  6. py正则与re模块

    正则表达式符号介绍 按照博客中的表格罗列的去记即可 了解 \w,\s,\d与\W,\S,\D相反的匹配关系(对应的两者结合就是匹配全局) \t匹配制表符 \b匹配结尾的指定单词 优先掌握 ^:以什么什 ...

  7. 【PyCharm】PyCharm设置深色背景

    操作步骤 1.依次点击File->Settings->Appearance&Behavior->Appearance 2.选择Theme为Darcula

  8. 【Python】容器:列表(list)/字典(dict)/元组(tuple)/集合(set)

    三.Python容器:列表(list)/字典(dict)/元组(tuple)/集合(set) 1.列表(list) 1.1 什么是列表 是一个'大容器',可以存储N多个元素简单来说就是其他语言中的数组 ...

  9. UGUI UI拖拽,UI连线。

    1.拖拽 public class Item : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler { public Re ...

  10. sap 付费支持 fico付费求助

    从事SAP多年,SAP付费求助,fico有偿服务,月结问题 有偿处理,物料分类账异常处理 每次每个问题最少 500 CNY , 有需要的联系 wx :erpworld sap  fico  有偿服务