一个基于线程池和epoll的IO事件管理器
前面几篇博客介绍了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事件管理器的更多相关文章
- JDFS:一款分布式文件管理实用程序第一篇(线程池、epoll、上传、下载)
一 前言 截止目前,笔者在博客园上面已经发表了3篇关于网络下载的文章,这三篇博客实现了基于socket的http多线程远程断点下载实用程序.笔者打算在此基础上开发出一款分布式文件管理实用程序,截止目前 ...
- 【Java TCP/IP Socket】基于线程池的TCP服务器(含代码)
了解线程池 在http://blog.csdn.net/ns_code/article/details/14105457(读书笔记一:TCP Socket)这篇博文中,服务器端采用的实现方式是:一个客 ...
- 浅谈线程池(中):独立线程池的作用及IO线程池
原文地址:http://blog.zhaojie.me/2009/07/thread-pool-2-dedicate-pool-and-io-pool.html 在上一篇文章中,我们简单讨论了线程池的 ...
- 设计模式:基于线程池的并发Visitor模式
1.前言 第二篇设计模式的文章我们谈谈Visitor模式. 当然,不是简单的列个的demo,我们以电商网站中的购物车功能为背景,使用线程池实现并发的Visitor模式,并聊聊其中的几个关键点. 一,基 ...
- 基于线程池的多并发Socket程序的实现
Socket“服务器-客户端”模型的多线程并发实现效果的大体思路是:首先,在Server端建立“链接循环”,每一个链接都开启一个“线程”,使得每一个Client端都能通过已经建立好的线程来同时与Ser ...
- requests模块session处理cookie 与基于线程池的数据爬取
引入 有些时候,我们在使用爬虫程序去爬取一些用户相关信息的数据(爬取张三“人人网”个人主页数据)时,如果使用之前requests模块常规操作时,往往达不到我们想要的目的,例如: #!/usr/bin/ ...
- Python网络爬虫之cookie处理、验证码识别、代理ip、基于线程池的数据爬去
本文概要 session处理cookie proxies参数设置请求代理ip 基于线程池的数据爬取 引入 有些时候,我们在使用爬虫程序去爬取一些用户相关信息的数据(爬取张三“人人网”个人主页数据)时, ...
- 基于线程池的多线程售票demo2.0(原创)
继上回基于线程池的多线程售票demo,具体链接: http://www.cnblogs.com/xifenglou/p/8807323.html以上算是单机版的实现,特别使用了redis 实现分布式锁 ...
- 基于线程池的多线程售票demo(原创)
废话不多说,直接就开撸import org.springframework.util.StopWatch;import java.util.concurrent.*;/** * 基于线程池实现的多线程 ...
- 发一个可伸缩线程池大小的python线程池。已通过测试。
发一个可伸缩线程池大小的线程池. 当任务不多时候,不开那么多线程,当任务多的时候开更多线程.当长时间没任务时候,将线程数量减小到一定数量. java的Threadpoolexcutor可以这样,py的 ...
随机推荐
- 软件工程日报七——checkbox的使用
今天学了checkbox的使用 activity_main.xml文件为 <?xml version="1.0" encoding="utf-8"?> ...
- GNN学习(一):基础知识
1 # !usr/bin/env python 2 # -*- coding:utf-8 _*- 3 # @Time :2022/8/20 10:46 4 # @Author: VVZ 5 # @Fi ...
- webpack5用url-loader(file-loader)处理图片和img-loader压缩图片
webpack-cli 4.0的版本和 webpack-dev-server 3.11.0的版本不兼容,只能用webpack@5.0 + webpack-cli@3.3.12 + webpack-de ...
- docker 安装 elasticsearch7.6.2 kibana7.6.2
[root@abcdefg bin]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 6896f6e3202c ...
- JAVA学习笔记-07
局部内部类不能定义静态成员. 内部类定义在局部时: 1不可以被成员修饰符修饰 2.可以直接访问玩不类中的成员,因为还持有外部类中的引用 但是不可以访问它所在的局部中的变量,只能访问被final修饰的局 ...
- Influxdb客户端使用
Influxdb客户端使用 1. 简介 Influxdb在网络上可以搜索到一个叫InfluxdbStudio的工具,但只能在window下使用,有时在其他系统下做简单数据查询时就比较麻烦,下面以Inf ...
- shell - scriptfifo
shell1: mkfifo xxfifo script -f xxfifo cmd cmd exit -----主动退出 shell2: cat xxfifo 可以看到shell1的所有操作. ...
- Bat命令进行压缩X文件夹下的文件夹及文件
输出日志 echo off for /d %%i in (D:\project101\trunk\x_client\xProject\Assets\AssetBundles\Android~\*.) ...
- wpf TreeView右键选中节点弹菜单
<TreeView x:Name="CustomTreeView" Canvas.Top="1" Canvas.Bottom="1" ...
- 论文笔记:To BLOB or Not To BLOB: Large Object Storage in a Database or a Filesystem?
论文笔记:To BLOB or Not To BLOB: Large Object Storage in a Database or a Filesystem? Conclusion 如果对象平均大小 ...