文章目录
- 引言
- [一、OneThreadOneLoop 模型的核心思想](#一、OneThreadOneLoop 模型的核心思想)
- 二、需要新增的核心文件
-
- [1. Channel.hpp:事件封装器](#1. Channel.hpp:事件封装器)
- [2. EventLoop.hpp:线程内的事件循环](#2. EventLoop.hpp:线程内的事件循环)
- [3. EventLoopThreadPool.hpp:事件循环线程池](#3. EventLoopThreadPool.hpp:事件循环线程池)
- 三、需要修改的文件
-
- [1. ReactorServer.hpp:主服务类](#1. ReactorServer.hpp:主服务类)
- [2. ClientHandler.hpp:客户端连接处理器](#2. ClientHandler.hpp:客户端连接处理器)
- 四、总结
引言
在前面的两篇文章中,我们实现了单 Reactor 单线程模型和单 Reactor 线程池模型。单 Reactor 线程池通过将 IO 操作分发到线程池处理,解决了单线程模型的性能瓶颈,但在高并发场景下,主线程的 Reactor 仍然可能成为新的瓶颈。本文将介绍如何将单 Reactor 线程池模型演进为更高效的 OneThreadOneLoop 模型,以及关键组件Channel的设计与价值。
一、OneThreadOneLoop 模型的核心思想
**OneThreadOneLoop(一个线程一个事件循环)**是高性能网络编程中的经典模型,其核心特点是:
- 主线程(主 Reactor)仅负责监听新连接
- 每个子线程拥有独立的事件循环(EventLoop)
- 新连接通过轮询策略分配给子线程的 EventLoop
- 连接的所有 IO 操作都在其绑定的子线程中处理,避免线程切换开销
相比单 Reactor 线程池,OneThreadOneLoop 的优势在于:
- 彻底避免多线程对同一连接的竞争
- 每个线程专注处理自己的事件循环,缓存友好性更好
- 简化线程同步逻辑,减少锁开销
二、需要新增的核心文件
1. Channel.hpp:事件封装器
Channel 是 OneThreadOneLoop 模型的关键组件,它封装了文件描述符(fd) 、关注的事件 、就绪的事件 以及对应的事件回调。
cpp
#ifndef __CHANNEL_HPP__
#define __CHANNEL_HPP__
#include <functional>
#include <sys/epoll.h>
// 事件回调类型定义
using EventCallback = std::function<void()>;
// Channel封装:负责管理单个文件描述符的IO事件
class Channel {
public:
Channel(int fd) : _fd(fd), _events(0), _revents(0) {}
~Channel() = default;
// 获取文件描述符
int fd() const { return _fd; }
// 设置/获取关注的事件
void setEvents(uint32_t events) { _events = events; }
uint32_t events() const { return _events; }
// 设置/获取就绪的事件
void setRevents(uint32_t revents) { _revents = revents; }
uint32_t revents() const { return _revents; }
// 设置事件回调
void setReadCallback(EventCallback cb) { _read_cb = std::move(cb); }
void setWriteCallback(EventCallback cb) { _write_cb = std::move(cb); }
void setErrorCallback(EventCallback cb) { _error_cb = std::move(cb); }
// 处理事件(核心方法)
void handleEvent() {
if (_revents & EPOLLERR) {
if (_error_cb) _error_cb();
return;
}
if (_revents & (EPOLLIN | EPOLLPRI)) {
if (_read_cb) _read_cb(); // 触发读事件回调
}
if (_revents & EPOLLOUT) {
if (_write_cb) _write_cb(); // 触发写事件回调
}
}
// 事件操作辅助函数
void enableReading() { _events |= EPOLLIN; }
void enableWriting() { _events |= EPOLLOUT; }
void disableWriting() { _events &= ~EPOLLOUT; }
void disableAll() { _events = 0; }
bool isWriting() const { return _events & EPOLLOUT; }
private:
int _fd; // 关联的文件描述符
uint32_t _events; // 关注的事件(如EPOLLIN、EPOLLOUT)
uint32_t _revents; // 就绪的事件(由epoll返回)
EventCallback _read_cb; // 读事件回调
EventCallback _write_cb; // 写事件回调
EventCallback _error_cb; // 错误事件回调
};
#endif /* __CHANNEL_HPP__ */
Channel 的核心功能:
- 作为 fd 与事件循环的中间层,隔离了底层 IO 复用(如 epoll)的细节
- 将事件(读 / 写 / 错误)与对应的处理逻辑(回调函数)绑定
- 提供简洁的接口控制关注的事件(如启用 / 禁用读 / 写事件)
为什么说 Channel 并非必须,但却是最佳实践?
- 理论上,我们可以直接在 EventLoop 中处理 fd 和事件回调,但会导致代码耦合严重
- Channel 通过封装,实现了 "事件 - 回调" 的解耦,让 EventLoop 只需负责事件分发,无需关心具体业务
- 在工程实践中,Channel 大幅提升了代码的可维护性和扩展性(例如新增事件类型时只需修改 Channel)
2. EventLoop.hpp:线程内的事件循环
每个线程拥有一个 EventLoop,负责监听该线程上所有 Channel 的事件,并分发处理。
cpp
#ifndef __EVENT_LOOP_HPP__
#define __EVENT_LOOP_HPP__
#include "EpollDemultiplexer.hpp"
#include "Channel.hpp"
#include <map>
#include <thread>
#include <mutex>
#include <vector>
// 事件循环类:每个线程一个EventLoop
class EventLoop {
public:
EventLoop() : _demux(), _thread_id(std::this_thread::get_id()), _running(false) {}
~EventLoop() { stop(); }
// 禁用拷贝
EventLoop(const EventLoop&) = delete;
EventLoop& operator=(const EventLoop&) = delete;
// 启动事件循环
void start() {
_running = true;
loop();
}
// 停止事件循环
void stop() {
_running = false;
}
// 判断当前线程是否是事件循环线程(线程安全检查)
bool isInLoopThread() const {
return std::this_thread::get_id() == _thread_id;
}
// 注册Channel(将fd和事件注册到epoll)
void updateChannel(Channel* channel) {
int fd = channel->fd();
if (_channels.find(fd) == _channels.end()) {
_channels[fd] = channel;
_demux.addEvent(fd, channel->events());
} else {
_demux.modifyEvent(fd, channel->events());
}
}
// 移除Channel
void removeChannel(Channel* channel) {
int fd = channel->fd();
if (_channels.find(fd) != _channels.end()) {
_demux.removeEvent(fd);
_channels.erase(fd);
}
}
private:
// 事件循环主体
void loop() {
std::vector<epoll_event> events(1024);
while (_running) {
// 等待事件就绪
int ready = _demux.waitEvents(events, 1000);
if (ready < 0) continue;
// 分发事件到对应Channel
for (int i = 0; i < ready; ++i) {
int fd = events[i].data.fd;
auto it = _channels.find(fd);
if (it != _channels.end()) {
it->second->setRevents(events[i].events); // 设置就绪事件
it->second->handleEvent(); // 触发Channel的事件处理
}
}
}
}
private:
EpollDemultiplexer _demux; // 多路复用器(封装epoll)
std::map<int, Channel*> _channels; // fd到Channel的映射
std::thread::id _thread_id; // 事件循环所在线程ID
bool _running; // 运行状态
};
#endif /* __EVENT_LOOP_HPP__ */
3. EventLoopThreadPool.hpp:事件循环线程池
管理多个 EventLoop 线程,负责线程的创建和轮询分配新连接。
cpp
#ifndef __EVENT_LOOP_THREAD_POOL_HPP__
#define __EVENT_LOOP_THREAD_POOL_HPP__
#include "EventLoop.hpp"
#include <vector>
#include <memory>
#include <thread>
#include <functional>
#include <mutex>
#include <condition_variable>
// 事件循环线程
class EventLoopThread {
public:
EventLoopThread() : _loop(nullptr), _running(false) {}
~EventLoopThread() {
if (_running) {
_running = false;
if (_thread.joinable()) {
_thread.join();
}
}
}
// 启动线程并返回事件循环
EventLoop* startLoop() {
_running = true;
_thread = std::thread(&EventLoopThread::threadFunc, this);
// 等待事件循环初始化完成
std::unique_lock<std::mutex> lock(_mutex);
_cond.wait(lock, [this]() { return _loop != nullptr; });
return _loop;
}
private:
// 线程函数:运行事件循环
void threadFunc() {
EventLoop loop; // 线程局部的事件循环
{
std::lock_guard<std::mutex> lock(_mutex);
_loop = &loop;
_cond.notify_one(); // 通知主线程循环已初始化
}
loop.start(); // 进入事件循环
_loop = nullptr;
}
private:
EventLoop* _loop; // 事件循环
bool _running; // 运行状态
std::thread _thread; // 线程
std::mutex _mutex; // 互斥锁
std::condition_variable _cond; // 条件变量
};
// 事件循环线程池
class EventLoopThreadPool {
public:
EventLoopThreadPool(size_t numThreads) : _num_threads(numThreads), _next_loop(0) {}
// 启动线程池
void start() {
for (size_t i = 0; i < _num_threads; ++i) {
auto thread = std::make_unique<EventLoopThread>();
_loops.push_back(thread->startLoop()); // 保存每个线程的EventLoop
_threads.push_back(std::move(thread));
}
}
// 轮询获取下一个事件循环(负载均衡)
EventLoop* getNextLoop() {
if (_loops.empty()) return nullptr;
EventLoop* loop = _loops[_next_loop];
_next_loop = (_next_loop + 1) % _loops.size(); // 轮询索引
return loop;
}
private:
size_t _num_threads; // 线程数量
std::vector<EventLoop*> _loops; // 事件循环列表
std::vector<std::unique_ptr<EventLoopThread>> _threads; // 线程列表
size_t _next_loop; // 下一个要使用的事件循环索引
};
#endif /* __EVENT_LOOP_THREAD_POOL_HPP__ */
三、需要修改的文件
1. ReactorServer.hpp:主服务类
主要修改点:
- 引入 EventLoopThreadPool 管理子线程的事件循环
- 使用 Channel 管理监听套接字的事件(AcceptorChannel)
- 新连接通过线程池分配到子线程的 EventLoop
cpp
#ifndef __REACTOR_SERVER_HPP__
#define __REACTOR_SERVER_HPP__
#include "EpollDemultiplexer.hpp"
#include "ClientHandler.hpp"
#include "Socket.hpp"
#include "EventLoop.hpp"
#include "EventLoopThreadPool.hpp"
#include "Channel.hpp"
#include <map>
#include <memory>
#include <cstdint>
#include <atomic>
class ReactorServer {
public:
ReactorServer(uint16_t port, int backlog = 1024, size_t thread_num = 4)
: _port(port), _backlog(backlog), _main_loop(),
_thread_pool(thread_num), _is_running(false),
_listen_socket(std::make_unique<TcpSocket>()) {}
~ReactorServer() {
Stop();
}
// 初始化服务器
bool Init() {
if (_is_running) {
std::cerr << "服务器已在运行中" << std::endl;
return false;
}
try {
// 创建并配置监听Socket
_listen_socket->SocketOrDie();
// 设置端口复用
int opt = 1;
setsockopt(_listen_socket->Fd(), SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
_listen_socket->BindOrDie(_port);
_listen_socket->ListenOrDie(_backlog);
_listen_socket->SetNonBlocking();
// 初始化Acceptor的Channel(核心修改)
initAcceptor();
// 启动事件循环线程池
_thread_pool.start();
return true;
} catch (...) {
std::cerr << "服务器初始化失败" << std::endl;
return false;
}
}
// 启动服务器
bool Start() {
if (_is_running) return false;
_is_running = true;
std::cout << "服务器启动成功,监听端口 " << _port << "..." << std::endl;
_main_loop.start(); // 启动主事件循环
return true;
}
// 停止服务器
void Stop() {
if (!_is_running) return;
_is_running = false;
_main_loop.stop();
_clients.clear();
_acceptor_channel.reset();
_listen_socket->Close();
}
private:
// 初始化Acceptor的Channel
void initAcceptor() {
int listen_fd = _listen_socket->Fd();
_acceptor_channel = std::make_unique<Channel>(listen_fd);
// 设置读事件回调:接收新连接
_acceptor_channel->setReadCallback([this]() {
std::string client_ip;
int client_fd = _listen_socket->Accept(&client_ip);
if (client_fd >= 0) {
onNewConnection(client_fd, client_ip);
}
});
// 关注读事件
_acceptor_channel->enableReading();
_main_loop.updateChannel(_acceptor_channel.get()); // 注册到主循环
}
// 处理新连接(核心修改:分配到子线程的EventLoop)
void onNewConnection(int client_fd, const std::string& client_ip) {
std::cout << "新连接:IP=" << client_ip << ", fd=" << client_fd << std::endl;
// 选择一个子事件循环
EventLoop* loop = _thread_pool.getNextLoop();
if (!loop) {
close(client_fd);
return;
}
// 创建客户端处理器并绑定到选中的事件循环
auto client_handler = std::make_shared<ClientHandler>(
client_fd, *loop,
[this, client_ip](int fd, const std::string& data) {
onMessage(fd, client_ip, data);
},
[this](int fd) {
onClose(fd);
});
_clients[client_fd] = client_handler;
}
// 处理客户端消息
void onMessage(int fd, const std::string& client_ip, const std::string& data) {
std::cout << "[" << client_ip << "][fd=" << fd << "] 收到数据: " << data << std::endl;
auto it = _clients.find(fd);
if (it != _clients.end()) {
it->second->sendData("服务器回显: " + data);
}
}
// 处理客户端关闭
void onClose(int fd) {
std::cout << "客户端断开:fd=" << fd << std::endl;
_clients.erase(fd);
}
private:
uint16_t _port; // 监听端口
int _backlog; // 监听队列大小
EventLoop _main_loop; // 主事件循环(处理新连接)
EventLoopThreadPool _thread_pool; // 事件循环线程池
std::atomic<bool> _is_running; // 运行状态标识
std::unique_ptr<TcpSocket> _listen_socket; // 监听Socket
std::unique_ptr<Channel> _acceptor_channel; // Acceptor的Channel
std::map<int, std::shared_ptr<ClientHandler>> _clients; // 客户端处理器映射
};
#endif /* __REACTOR_SERVER_HPP__ */
2. ClientHandler.hpp:客户端连接处理器
主要修改点:
- 使用 Channel 管理客户端 fd 的读写事件
- 将 IO 事件绑定到所属子线程的 EventLoop
cpp
#ifndef __CLIENT_HANDLER_HPP__
#define __CLIENT_HANDLER_HPP__
#include "EventHandler.hpp"
#include "EventLoop.hpp"
#include "Channel.hpp"
#include "Socket.hpp"
#include <functional>
#include <string>
#include <mutex>
// 客户端处理器:处理数据读写事件
class ClientHandler : public EventHandler {
public:
using Ptr = std::shared_ptr<ClientHandler>;
using MessageCallback = std::function<void(int, const std::string&)>;
using CloseCallback = std::function<void(int)>;
ClientHandler(int client_fd, EventLoop& loop, MessageCallback msg_cb, CloseCallback close_cb)
: EventHandler(client_fd), _loop(loop), _msg_cb(std::move(msg_cb)),
_close_cb(std::move(close_cb)), _client_socket(client_fd), _channel(client_fd) {
// 初始化Channel回调(核心修改)
_channel.setReadCallback([this]() { handleRead(); });
_channel.setWriteCallback([this]() { handleWrite(); });
_channel.setErrorCallback([this]() { handleError(); });
// 开始关注读事件
_channel.enableReading();
_loop.updateChannel(&_channel); // 注册到子线程的EventLoop
}
~ClientHandler() {
_loop.removeChannel(&_channel); // 从事件循环中移除
}
// 处理读事件
void handleRead() override {
std::string data;
ssize_t n = _client_socket.Recv(&data);
if (n < 0) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
_close_cb(_fd);
}
return;
}
if (n == 0) { // 客户端断开
_close_cb(_fd);
return;
}
_msg_cb(_fd, data); // 触发消息回调
}
// 处理写事件(使用缓冲区实现异步发送)
void handleWrite() override {
std::lock_guard<std::mutex> lock(_buf_mutex);
if (_send_buf.empty()) {
_channel.disableWriting(); // 无数据可写,取消关注
_loop.updateChannel(&_channel);
return;
}
// 发送数据
ssize_t n = _client_socket.Send(_send_buf);
if (n > 0) {
_send_buf.erase(0, n);
}
// 剩余数据继续监听可写事件
if (!_send_buf.empty()) {
_channel.enableWriting();
} else {
_channel.disableWriting();
}
_loop.updateChannel(&_channel);
}
void handleError() override {
_close_cb(_fd);
}
// 发送数据(线程安全)
void sendData(const std::string& data) {
bool need_wakeup = false;
{
std::lock_guard<std::mutex> lock(_buf_mutex);
need_wakeup = _send_buf.empty(); // 缓冲区为空时需要唤醒写事件
_send_buf += data;
}
if (need_wakeup) {
_channel.enableWriting();
_loop.updateChannel(&_channel);
}
}
private:
EventLoop& _loop; // 所属的事件循环(子线程)
MessageCallback _msg_cb; // 消息处理回调
CloseCallback _close_cb; // 关闭回调
std::string _send_buf; // 待发送数据缓冲区
std::mutex _buf_mutex; // 缓冲区互斥锁
TcpSocket _client_socket; // 客户端Socket封装
Channel _channel; // 客户端Channel(核心组件)
};
#endif /* __CLIENT_HANDLER_HPP__ */
四、总结
从单 Reactor 线程池到 OneThreadOneLoop 的演进,核心变化是:
- 引入Channel组件,实现事件与回调的解耦
- 每个子线程拥有独立的EventLoop,负责本线程的事件循环
- 新连接通过线程池的轮询策略分配给子线程,实现负载均衡
这种模型在高并发场景下性能优势明显,也是许多高性能网络库(如 libevent、muduo)的核心设计。Channel 虽然不是实现事件驱动的必需组件,但它通过封装细节、解耦代码,成为了工程实践中的最佳选择。