当前位置: 首页 > news >正文

[muduo] docs | 配置教程 | EventLoop | Thread

链接:https://github.com/chenshuo/muduo?tab=readme-ov-file

Muduo 是一个基于 C++11 的多线程 Linux 服务器网络库,采用事件驱动模式。

它提供了一个简单易用的网络编程接口,支持 TCP 和 UDP 协议,并且具有良好的性能和可扩展性。

Main Function Points

  • 基于反应堆模式的事件驱动网络库
  • 支持 TCP 和 UDP 协议
  • 提供简单易用的网络编程接口
  • 具有良好的性能和可扩展性
  • 支持多线程编程

Technology Stack

  • C++11
  • Linux 操作系统
  • Boost 库 (仅用于 boost::any)

主要功能

1. 事件驱动架构

  • 基于 Reactor 反应堆模式 实现,通过非阻塞 I/O 和多路复用技术(如 epoll)高效处理网络事件
  • 事件循环(EventLoop)机制自动调度读写事件,避免轮询造成的 CPU 浪费

2. 协议支持

  • 原生支持 TCP 长连接全生命周期管理(连接建立/数据传输/连接关闭)
  • 提供 UDP 协议 的异步通信能力,适用于实时性要求高的场景

3. 线程模型

  • 多线程支持:通过 ThreadPool 实现任务队列与线程资源复用
  • 单线程-多进程多线程-单进程 混合模式可选,平衡并发性能与资源消耗
  • 关键组件(如 TcpConnection)保证线程安全,避免竞态条件

4. 高性能设计

  • 零拷贝技术减少数据在内核/用户空间的复制次数
  • 对象池(Buffer)复用内存资源,降低频繁分配释放的开销
  • 实测可承载 10万+ QPS(单机)

5. 开发友好性

  • 提供 简洁的 API 接口(如 TcpServer/TcpClient 封装类)
  • 内置常用工具:定时器(Timer)、日志系统(Logging)、线程同步原语
  • 通过回调机制Callback)实现业务逻辑解耦

补充:基于 C++11 标准开发,依赖 Linux 原生 API,仅使用 boost::any 实现类型擦除。


目录

muduo/
├── CMakeLists.txt           # 项目构建配置
├── LICENSE                  # BSD 许可证文件
├── README.md                # 项目说明文档
├── build/                   # 编译输出目录
├── cmake/                   # CMake 模块配置
├── examples/                # 示例代码
│   ├── asio/chat/           # Boost.Asio 对比示例
│   ├── simple/              # 基础用法示例
│   └── ...                  
├── muduo/                   # 核心库源代码
│   ├── base/                # 基础工具模块
│   │   ├── Date.{h,cc}      # 日期时间处理
│   │   ├── Logging.{h,cc}   # 日志系统
│   │   └── Thread.{h,cc}    # 线程封装
│   └── net/                 # 网络核心模块
│       ├── Buffer.{h,cc}    # 数据缓冲区
│       ├── Channel.{h,cc}   # 事件通道封装
│       ├── EventLoop.{h,cc} # 事件循环核心
│       ├── TcpServer.{h,cc} # TCP 服务端实现
│       └── ...              
├── tests/                   # 单元测试
│   ├── muduo_base_test      # 基础模块测试
│   └── ...                  
└── util/                    # 实用工具└── ...                  

说明:

  1. muduo/base/

    • 提供线程/日志/时间等基础设施
    • 例如 ThreadPool 实现线程复用,LogStream 支持流式日志
  2. muduo/net/

    • Reactor 模式核心实现
    • 包含 EventLoop (事件调度器) 和 TcpConnection (连接管理)
    • 通过 Poller 抽象支持 epoll/poll 等多路复用机制
  3. examples/

    • 包含从 Echo 服务到 HTTP 服务器的 20+ 示例
    • 每个示例独立演示特定功能模块的用法

💡 实际使用时需注意:

  1. 编译前需安装 CMake 和 Linux 开发环境
  2. 通过 ./build.sh 脚本一键编译库和示例
  3. 建议在 Ubuntu/CentOS 等主流发行版运行

docs:muduo

在这里插入图片描述
Muduo 是一个专为Linux 平台事件驱动型服务器编程设计的 C++ 网络库。

它采用**每个线程一个事件循环**模型,每个线程拥有自己的事件循环(EventLoop)

非阻塞方式处理TCP连接定时器的 I/O 事件。

该库提供了TCP服务器/客户端缓冲区I/O 多路复用(使用 epoll 或 poll)等抽象,用于简化高性能网络应用的开发。

目录

  1. 事件循环
  2. 线程
  3. 通道
  4. 轮询器
  5. 定时器队列
  6. TCP连接
  7. 缓冲区
  8. TCP服务器
  9. 线程池
  10. TCP客户端

引入:配置教程

1. 拉取 muduo 源码

git clone git@github.com:chenshuo/muduo.git

在这里插入图片描述

2. 安装依赖

对于 Ubuntu/Debian 系统,执行:

sudo apt update
sudo apt install g++ cmake make libboost-dev

3. 编译 muduo

进入 muduo 目录并编译:

cd muduo
./build.sh
sudo ./build.sh install

如果只需要静态库或动态库,可以只执行 ./build.sh,然后在 muduo/build-release 目录下找到编译好的库文件。


4. 编译代码

假设 muduo 安装到了 /usr/local/include/usr/local/lib,可以这样编译:

g++ test_eventloop.cpp -o test_eventloop -std=c++11 -I/usr/local/include -L/usr/local/lib -lmuduo_net -lmuduo_base -lpthread -lboost_thread -lboost_system

如果没有 sudo ./build.sh install,可以用如下方式指定 muduo 的头文件和库路径(假设在 muduo/build-release):

g++ test_eventloop.cpp -o test_eventloop -std=c++11 \-I./muduo \-I./muduo/build-release \-L./muduo/build-release/lib \-lmuduo_net -lmuduo_base -lpthread -lboost_thread -lboost_system

第一章:事件循环(EventLoop)

在这里插入图片描述

欢迎来到 Muduo 网络库系列!

Muduo 是一个专为高并发网络服务设计的 C++ 网络库。

其核心是一个称为 EventLoop 的概念。可以将 EventLoop 视为 Muduo 应用中保持一切顺畅运行的勤勉工蜂。

EventLoop 解决了什么问题?

  • 假设您在构建一个需要同时处理多个客户端的服务器。数据可能在任何时刻任意客户端连接到达。同时,可能需要在特定时间执行任务,例如检查空闲连接或发送定期更新

  • 如何在所有这些连接和定时器之间高效等待任何事件发生,并在事件发生时快速响应

  • 一个简单的方法可能是为每个客户端使用单独的线程 ,但对于数千个连接来说这会消耗过多资源。

  • 另一种方法是不断在循环中检查每个连接和定时器 ,但这是低效的(忙等待)。

这正是 EventLoop 的用武之地。


EventLoop:单线程接待员

muduo::net::EventLoop 是 Muduo 应用网络处理的核心组件。

它设计为在**单个专用线程**中运行。其主要职责是等待"事件"并将其"分发"到正确的处理代码。

EventLoop 想象成办公室中高效的接待员:

  • 办公室EventLoop 所在的线程
  • 接待员EventLoop)在此驻守等待
  • 访客到达(网络连接数据到达)或时钟到达特定时间(定时器触发)即为"事件"
  • 当事件发生时,接待员不直接处理,但清楚知道办公室中负责该访客或定时器(即您的"回调"或"处理器"),在同一办公室(线程)内将消息传递给正确负责人

这种"每个线程一个事件循环"模型是 Muduo 的基本原则。

它确保特定 EventLoop 管理的所有操作都在线程内发生,极大简化了线程安全性。


EventLoop 概念

让我们分解核心思想:

  1. 单线程单循环:每个 EventLoop 对象严格绑定到创建它的线程。同一线程不能有多个 EventLoop,且 EventLoop 必须始终在创建线程中使用。这对避免访问 EventLoop 管理的数据结构时的复杂锁问题至关重要。
  2. 事件循环周期(loop()EventLoop 的生命周期主要在 loop() 方法中度过。该方法进入循环:
    • 等待事件(底层使用 epollpoll 等机制)
    • 处理发生的事件(如套接字数据可读、定时器触发)
    • 执行需要延迟在该线程执行的任务
    • 循环重复
  3. 事件源EventLoop 等待哪些事件?
    • 套接字 I/O 事件(如数据可读、写缓冲区可用),通过 Channel 对象管理
    • 定时器事件(如"5秒后运行此函数"),通过 TimerQueue 管理
    • 其他线程提交的任务

使用 EventLoop:示例

#include "muduo/net/EventLoop.h"
#include <cstdio>
#include <unistd.h>void myCallback() {printf("回调执行! pid = %d\n", getpid());
}int main() {printf("主线程启动. pid = %d\n", getpid());muduo::net::EventLoop loop;printf("EventLoop 对象 'loop' 已创建\n");assert(muduo::net::EventLoop::getEventLoopOfCurrentThread() == &loop);printf("确认 'loop' 属于主线程\n");printf("调度 2 秒后执行 myCallback...\n");loop.runAfter(2.0, myCallback);printf("启动事件循环...\n");loop.loop();printf("事件循环结束\n");return 0;
}

运行
在这里插入图片描述

多线程示例

#include "muduo/net/EventLoop.h"
#include "muduo/base/Thread.h"
#include <cstdio>
#include <assert.h>void threadFunc() {printf("线程函数启动. pid = %d, tid = %d\n", getpid(), muduo::CurrentThread::tid());assert(muduo::net::EventLoop::getEventLoopOfCurrentThread() == NULL);muduo::net::EventLoop loop;printf("新线程中创建 EventLoop\n");assert(muduo::net::EventLoop::getEventLoopOfCurrentThread() == &loop);printf("启动新线程事件循环...\n");loop.loop();printf("新线程循环结束\n");
}int main() {printf("主线程启动. pid = %d, tid = %d\n", getpid(), muduo::CurrentThread::tid());muduo::Thread thread(threadFunc);printf("创建新线程...\n");thread.start();thread.join();printf("子线程结束\n");return 0;
}

运行
在这里插入图片描述

内部机制

EventLoop::loop() 核心流程:

  1. 轮询等待:通过 Poller 对象高效等待 I/O 事件
  2. 处理活跃通道:遍历有事件的 Channel 并执行回调
  3. 执行待处理任务:处理来自其他线程的队列任务
// 简化版 loop() 实现
void EventLoop::loop() {while (!quit_) {activeChannels_.clear();poller_->poll(kPollTimeMs, &activeChannels_); // 阻塞等待for (Channel* channel : activeChannels_) {channel->handleEvent(); // 处理事件}doPendingFunctors(); // 执行队列任务}
}

跨线程任务调度

通过 wakeupFd_ 实现线程间唤醒机制:

在这里插入图片描述

总结

特性描述优势
单线程单循环每个 EventLoop 严格绑定线程简化线程安全
loop() 方法核心循环:等待事件 → 处理 → 执行任务高效事件处理
Poller使用 epoll/poll 进行 I/O 多路复用避免忙等待
TimerQueue管理定时任务精确时间控制
wakeupFd_用于跨线程唤醒的特殊文件描述符实现线程间通信
任务队列存储待执行任务的线程安全队列安全跨线程任务提交
runInLoop智能判断立即执行或排队执行灵活的任务调度机制

下一章:线程 →


第二章:线程

在第一章:事件循环中,我们了解到EventLoop是Muduo网络处理的核心,且必须在专用线程中运行。

虽然可以在主应用线程中运行EventLoop,但实际服务器通常需要处理多个连接并执行后台任务,这需要多线程的支持。

在C++中直接使用原始POSIX线程(pthreads)进行手动线程管理可能非常复杂。这涉及线程创建参数传递生命周期管理正确清理等问题。这正是muduo::Thread的价值所在。
在这里插入图片描述

Thread解决了什么问题?

muduo::Thread为底层POSIX线程函数提供了简洁且符合C++风格的封装。

  • 简化了线程的创建和管理流程,让开发者专注于要在新线程中运行的函数,而不是繁琐的线程管理代码

    muduo::Thread的主要目标: 轻松在新线程中启动特定功能函数

    这个函数可以是任何逻辑,但在Muduo的上下文中,它通常是EventLoop对象的loop()方法。

Thread:简洁的线程封装器

muduo::Thread视为独立执行上下文(线程)的简单容器

当创建muduo::Thread对象时,需指定要在新线程中运行的功能函数

以下是muduo::Thread的核心设计理念:

  1. 封装性:将POSIX线程ID(pthread_t)和要运行的函数(ThreadFunc)打包到单个C++对象中
  2. 简化启动:通过简单的start()方法创建底层POSIX线程
  3. 合并等待join()方法允许创建线程,等待新线程完成执行
  4. 命名机制:可为线程赋予有意义的名称,这对调试非常有用(例如在日志或调试器中查看线程名称)
  5. 上下文传递:处理必要数据(如函数、名称和就绪信号机制)到新线程的入口点
  6. 异常处理:包含线程入口点的基本异常处理,以捕获意外错误并记录
  7. 线程ID(tid:提供获取操作系统级线程ID(pid_t)的便捷方式,并通过muduo::CurrentThread::tid()实现快速访问

使用Thread:在新线程中运行函数

最基本的用例是创建一个函数,并让muduo::Thread对象运行它。

定义要在单独线程中运行的简单函数:

#include <cstdio>
#include <unistd.h> // getpid
#include "muduo/base/CurrentThread.h" // muduo::CurrentThread::tid()// 该函数将在新线程中运行
void myThreadFunction() {printf("进入myThreadFunction. 进程ID: %d, 线程ID: %d\n",getpid(), muduo::CurrentThread::tid());// 执行某些工作...例如休眠sleep(3);printf("myThreadFunction执行完毕\n");
}

该函数仅打印进程和线程ID,然后休眠几秒。

muduo::CurrentThread::tid()是Muduo提供的辅助函数,用于获取当前线程的OS级线程ID(pid_t)。

现在看看如何使用muduo::Thread运行myThreadFunction

#include "muduo/base/Thread.h"
#include "muduo/base/CurrentThread.h"
#include <cstdio>
#include <unistd.h>// ...(上方myThreadFunction定义)...int main() {printf("主线程启动. 进程ID: %d, 线程ID: %d\n",getpid(), muduo::CurrentThread::tid());// 1. 创建Thread对象,传入要运行的函数muduo::Thread thread(myThreadFunction, "MyWorkerThread");printf("线程对象已创建\n");// 2. 启动新线程printf("正在启动新线程...\n");thread.start();// 主线程可在此执行其他工作// printf("主线程正在处理其他任务...\n");// sleep(1);// 3. 等待新线程结束printf("主线程等待新线程结束...\n");thread.join();printf("新线程已结束. 主线程退出\n");return 0;
}

运行此代码时:

  1. main函数在主线程启动
  2. 打印其PID和TID
  3. 创建名为threadmuduo::Thread对象,传入myThreadFunction和名称"MyWorkerThread"
  4. 调用thread.start(),创建新的OS线程并开始执行thread对象管理的代码
  5. 主线程立即继续执行,调用thread.join()等待新线程结束
  6. 新线程执行myThreadFunction,打印不同TID后休眠3秒
  7. 主线程检测到新线程结束后继续执行

运行:
在这里插入图片描述

在线程中运行EventLoop

如第一章所述,Muduo的常见模式是在每个线程中运行一个EventLoop

muduo::Thread是实现此模式的理想工具。

如何在独立线程中运行EventLoop

#include "muduo/net/EventLoop.h"
#include "muduo/base/Thread.h"
#include "muduo/base/CurrentThread.h"
#include <cstdio>
#include <assert.h>// 将在新线程运行的函数
void threadFunc() {printf("threadFunc启动. 进程ID: %d, 线程ID: %d\n", getpid(), muduo::CurrentThread::tid());assert(muduo::net::EventLoop::getEventLoopOfCurrentThread() == NULL);printf("确认新线程初始无EventLoop\n");muduo::net::EventLoop loop;printf("新线程中创建EventLoop对象\n");assert(muduo::net::EventLoop::getEventLoopOfCurrentThread() == &loop);printf("确认loop属于当前线程\n");printf("启动事件循环...\n");loop.loop(); // 阻塞直到调用quit()printf("事件循环结束\n");
}int main() {printf("主线程启动. 进程ID: %d, 线程ID: %d\n",getpid(), muduo::CurrentThread::tid());muduo::Thread thread(threadFunc, "EventLoopThread");printf("创建新线程...\n");thread.start();printf("主线程等待事件循环线程...\n");thread.join();printf("事件循环线程结束,主线程退出\n");return 0;
}

此模式(创建muduo::Thread运行设置并执行EventLoop的函数)是Muduo多线程网络I/O应用的基础。

每个此类线程都将拥有自己的EventLoop


Thread内部机制解析

muduo::Thread本质上是POSIX pthread_createpthread_join的封装。以下是调用thread.start()时的内部流程:

  1. 数据封装start()在堆上分配detail::ThreadData辅助对象,存储用户函数、线程名等
  2. 线程创建:调用pthread_create时传入内部入口函数detail::startThread
  3. 新线程初始化
    • 设置线程名(通过prctl系统调用)
    • 缓存线程ID到CurrentThread::t_cachedTid
    • 通过CountDownLatch通知主线程初始化完成
  4. 异常处理:使用try-catch块包裹用户函数调用
  5. 资源清理:用户函数执行完毕后删除辅助对象

代码:

muduo/base/Thread.h类定义:

class Thread : noncopyable {
public:typedef std::function<void ()> ThreadFunc; // 线程函数类型explicit Thread(ThreadFunc, const string& name = string());void start(); // 启动线程int join(); // 等待线程结束// ...其他成员...
};

muduo/base/Thread.cc核心逻辑:

// 简化版线程启动逻辑
void Thread::start() {detail::ThreadData* data = new detail::ThreadData(func_, name_, &tid_, &latch_);pthread_create(&pthreadId_, NULL, &detail::startThread, data);latch_.wait(); // 等待新线程初始化完成
}

CurrentThread命名空间提供线程本地存储:

namespace CurrentThread {__thread int t_cachedTid; // 线程本地存储的TID缓存void cacheTid() { t_cachedTid = detail::gettid(); // 系统调用获取真实TID}
}

总结

特性描述优势
线程函数封装使用std::function包装需在新线程执行的代码清晰分离线程定义与任务逻辑
原子化启动start()方法一键创建并启动POSIX线程简化线程生命周期管理
命名机制支持通过prctl设置线程名称提升调试效率
TID缓存CurrentThread::tid()提供快速线程ID访问避免频繁系统调用
异常安全在入口函数中包裹try-catch块防止线程意外终止
跨线程同步使用CountDownLatch确保线程初始化顺序保证资源安全访问

muduo::Thread为C++提供了健壮的线程操作抽象,其核心价值体现在:

  1. 简化线程管理:封装POSIX线程的复杂API,提供符合RAII原则的接口
  2. 支持事件循环模型:作为实现"单线程单循环"架构的基础设施
  3. 增强调试能力:通过线程命名和TID缓存机制提升可观测性
  4. 保证线程安全:严谨的初始化顺序控制和异常处理机制

理解muduo::Thread的工作原理是构建高性能网络应用的关键下一步,我们将探讨EventLoop如何通过Channel监听和处理I/O事件

第三章:通道 →

http://www.lryc.cn/news/572785.html

相关文章:

  • python实战项目76:51job数据采集与分析
  • 14.9 AI教学系统测试全攻略:模块化调试与5大模块实战指南
  • 服务网格安全(Istio):用零信任架构重构微服务通信安全
  • 【Linux驱动开发 ---- 4.2_平台设备(Platform Devices)概述】
  • 基于深度学习的智能视频行为识别系统:技术与实践
  • 解决Windows10没有Microsoft store微软商店
  • C#学习日记
  • 局域网文件共享及检索系统
  • 在小程序中实现上下左右拖动表格
  • js调用微信支付 第二步 获取access_token ——仙盟创梦IDE
  • 多线程八股
  • Docker Swarm 与 Docker Compose 对比解析
  • 汽车整车厂如何用数字孪生系统打造“透明车间”
  • python高校工作室管理系统
  • 晨控CK-FR06与西门子PLC配置Modbus TCP通讯连接操作手册
  • Cmake入门及CMakeLists.txt 语法介绍
  • linux线程同步
  • 数据结构第八章(六)-置换选择排序和最佳归并树
  • P12894 [蓝桥杯 2025 国 Java B] 智能交通信号灯
  • 【机器学习实战笔记 12】集成学习:AdaBoost算法
  • 数字IC后端实现之Setup Violation修复案例(DataClock Tree ECO修复手段)
  • 目标检测新升级:用YOLOv8打造密度视频热力图可视化
  • 2025 年拓客系统排行榜
  • ceph 通过 crush rule 修改故障域
  • 装饰器模式深度解析:Java设计模式实战指南与动态功能扩展最佳实践
  • tkinter 的 pack() 布局管理器学习指南
  • 商业秘密被公开后的损失计算:从法律规定到司法实践的深度解析
  • npm下载离线依赖包
  • leetcode 291. Word Pattern II和290. Word Pattern
  • Vue 比较两个数组对象,页面展示差异数据值