一个基于线程池和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的 ...
随机推荐
- python_lib_0001_decorator_print_log
def decorator_log_funcname( func ): def wrapper(*arg, **kw): print("") ...
- SpringBoot为什么这么火?
1. 总的设计原则是""默认大于配置"" 2. Starter机制,开箱即用,默认的配置和依赖都是默认加载的 3. SpringBoot是Spring的子类, ...
- CUDA kernel使用printf调试,一定要加cudaDeviceSynchronize
#include <stdio.h> __global__ void print() { printf("Hello from (%d, %d)\n", blockId ...
- tomcat前后端项目部署及调优
第1章 tomcat简介Tomcat是Apache软件基金会(Apache Software Foundation)的Jakarta项目中的一个核心项目,由Apache,Sun和其他一些公司及个人共同 ...
- Security Bulletin: IBM WebSphere Application Server is vulnerable to a remote code execution vulnerability (CVE-2023-23477)
Skip to content Support DownloadsDocumentationForumsCasesMonitoringManage support account ...
- 字符串散列成GUID
https://stackoverflow.com/questions/2190890/how-can-i-generate-a-guid-for-a-string
- 如何在 Visual Studio 中自定义 Docker 容器 和 使用 Docker Compose 为 Visual Studio 创建多容器应用
https://learn.microsoft.com/zh-cn/visualstudio/containers/container-build?WT.mc_id=visualstudio_cont ...
- What is REST and Restful?
什么是rest 和 restful? 提出rest的作者,目的:符合框架原理的情况下,理解和评估以网络为基础的应用软件的架构设计,得到一个功能强,性能好,适宜通讯的架构. Fielding将他对互联网 ...
- fiddler 调试
如果本地代理js发现跨域,需要手动修改自定义规则 1 static function OnBeforeResponse(oSession: Session) { 2 if (m_Hide304s &a ...
- fabric学习笔记2
fabric学习笔记2 20201303张奕博 2023.1.10 Fabric云服务器搭建 参考视频:视频链接:https://www.bilibili.com/video/BV1gV4y1M7q3 ...