仿muduo库实现高并发服务器---线程池模块Eventloop的实现

目录

一、前置知识

eventfd的介绍

EventLoop的整体介绍

Eventloop处理模块流程

总结

二、成员变量

三、成员函数


一、前置知识

eventfd的介绍

eventfd:创建一个描述符用于实现时间通知本质是在内核里面管理的计数器,创建eventfd就会在内核中写入一个数字---用于表示事件通知次数,可以使用read进行数据的读取,读取到的数据就是通知次数

假设每次给eventfd中写入1,就表示通知一次,连续写了三次之后,read读数字就是3,读取之后计数清零

用处:Eventloop中实现消息通知机制

函数:

int eventfd(unsigned int initval, int flags)

参数:

initval:计数初值

flag:EFD_CLOEXEC 禁止进程复制

EFD_NONBLOCK 启动非阻塞属性

返回值:返回一个文件描述符用于操作

eventfd也是通过read write close进行操作

read/write进行IO的时候只能是一个8字节数据

EventLoop的整体介绍

Eventloop:进行事件监控,以及事件处理的模块

关键点: 这个模块是和线程一一对应的,监控了一个链接,而这个链接一旦就绪,就要进行数据处理,就会存在线程安全,因此我们需要将一个链接的事件监控,以及链接事件处理等其他动作放在同一个线程中进行

如何保证一个链接的所有操作都在eventloop对应的线程中进行

给eventloop模块中,添加一个任务队列,对所有操作都进行一次封装,将链接的操作当作任务添加到任务队列中

Eventloop处理模块流程

1、在线程中对于描述符进行监控

2、有描述符就绪,则对描述符进行事件处理保证处理回调函数的操作都在同一个流程中

3、所有的就绪事件处理完了,在将队列中的任务一一执行

总结

1、事件监控

使用Poller模块(有事件就绪则进行事件处理)

2、执行任务队列中的任务

一个线程安全的队伍

注意:有可能因为等待IO描述符事件就绪,导致执行流流程阻塞,这时候任务队列的任务得不到执行因此得有一个事件通知东西,唤醒阻塞

二、成员变量

1、线程id _thread_id

2、唤醒IO事件监控导致的阻塞 _event_fd

3、创建一个_event_fd的事件描述 std::unique_ptr<Channel> _event_channel;

4、进行所有描述付的事件监控 Poller _poller;

5、任务池 std::vector<Functor> _tasks;

6、实现线程安全 std::mutex _mutex;

当事件就绪,需要处理,处理过程中,如果对链接要进行某些操作的时候,这些操作必须在Eventloop线程中执行的,保证对链接各项操作都是线程安全的

(1)本身就在线程中(立即执行)

(2)不在 (需要加入任务池中)

using Functor = std::function<void()>; std::thread::id _thread_id; int _event_fd; Poller _poller; std::unique_ptr<Channel> _event_channel; std::vector<Functor> _task; std::mutex _mutex;

三、成员函数

1、执行线程池中所有的任务

void RunAllTask() { std::vector<Functor> functor; { std::unique_lock<std::mutex> _lock(_mutex); _task.swap(functor); } for (auto &f : functor) { f(); } return; }

2、创建eventfd

static int CreatEventFd() { int efd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); if (efd < 0) { ERR_LOG("EVENTFD FAILED"); abort(); } return efd; }

3、读取eventfd

void ReadEventFd() { uint64_t res = 0; int ret = read(_event_fd, &res, sizeof(res)); if (ret < 0) { if (errno == EINTR ||errno == EAGAIN) { return; } ERR_LOG("READ FAILED"); abort(); } return; }

4、初始化Eventloop

这里Channel内部有Eventloop的执政完成监控的更新

5、Start

三步走

事件监控

就绪事件处理

执行任务

void Start() { while (1) { std::vector<Channel *> active; _poller.Poll(&active); for (auto &channel : active) { channel->HandleEvents(); } RunAllTask(); } }

6、判断当前线程是否是EventLoop对应的线程

bool IsInloop() { return (_thread_id == std::this_thread::get_id()); }

void AssertInloop() { assert(_thread_id == std::this_thread::get_id()); }

7、判断将要执行的任务是否处于当前线程中,如果是则执行,如果不是则压入队列

void RunInLoop(const Functor &cd) { if (IsInloop() == true) { return cd(); } return QueueInLoop(cd); }

8、将操作压入任务池

void QueueInLoop(const Functor &cd) { { std::unique_lock<std::mutex> _lock(_mutex); _task.push_back(cd); } WeakUpEventFd(); }

9、唤醒线程

就是向线程中写入一个event_fd;

9、添加/修改描述符的事件监控

void UpdateEvent(Channel *channel) { return _poller.UpdateEvent(channel); }

10、移除描述符的事件监控

void RemoveEvent(Channel *channel) { return _poller.RemoveEvent(channel); }