muduo网络库——ThreadPool

article/2025/11/6 17:10:15

模型

源码分析

1)接口

class ThreadPool : noncopyable
{public:typedef std::function<void ()> Task;explicit ThreadPool(const string& nameArg = string("ThreadPool"));~ThreadPool();void setMaxQueueSize(int maxSize) { maxQueueSize_ = maxSize; }  // 设置队列大小void setThreadInitCallback(const Task& cb)                      // 设置回调{ threadInitCallback_ = cb; }void start(int numThreads);   // numThreads为要创建的子线程数目,开启线程void stop();                  // 关闭线程const string& name() const{ return name_; }size_t queueSize() const;void run(Task f);             // 任务执行函数
};

2)成员变量

private:bool isFull() const REQUIRES(mutex_);    void runInThread();Task take();mutable MutexLock mutex_;   //互斥锁Condition notEmpty_ GUARDED_BY(mutex_);  // 条件变量: 非空判断Condition notFull_ GUARDED_BY(mutex_);   // 条件变量: 非满判断string name_;Task threadInitCallback_;                // 回调函数std::vector<std::unique_ptr<muduo::Thread>> threads_;  // 线程组 使用智能指针维护单个线程对象std::deque<Task> queue_ GUARDED_BY(mutex_);  //双向队列 维护要执行的任务size_t maxQueueSize_;bool running_;  //是否运行标志 

3)线程启停

void ThreadPool::start(int numThreads)
{assert(threads_.empty());running_ = true;threads_.reserve(numThreads);for (int i = 0; i < numThreads; ++i){char id[32];snprintf(id, sizeof id, "%d", i+1);threads_.emplace_back(new muduo::Thread(std::bind(&ThreadPool::runInThread, this), name_+id)); //这里使用emplace_back(c++11)是为了提高插入性能,对比push_backthreads_[i]->start();   // 开启numThreads个线程加入线程池,等待有任务时进行调度}if (numThreads == 0 && threadInitCallback_){threadInitCallback_();}
}void ThreadPool::stop()
{{MutexLockGuard lock(mutex_);  // 线程停止时加锁running_ = false;notEmpty_.notifyAll();notFull_.notifyAll();}for (auto& thr : threads_){thr->join();}
}

说明:线程开始时不需要加锁,是因为此时只是创建空线程,并没有具体的任务来进行调度,故为单线程,不存在数据被多线程改变的情况。stop的时候有共享数据的修改,故需要加锁。

4)存任务

//注意: 存任务时一般需要进行队列非满判断; 取任务时一般需要进行队列非空判断
void ThreadPool::run(Task task)
{if (threads_.empty()){task();}else{MutexLockGuard lock(mutex_);while (isFull() && running_)  {notFull_.wait();  }if (!running_) return;assert(!isFull());queue_.push_back(std::move(task));notEmpty_.notify();  //非满通知 注意这里时notify(),而不是notifyAll()}
}

5)取任务

ThreadPool::Task ThreadPool::take()
{MutexLockGuard lock(mutex_);// always use a while-loop, due to spurious wakeupwhile (queue_.empty() && running_){notEmpty_.wait();}Task task;if (!queue_.empty()){task = queue_.front();queue_.pop_front();if (maxQueueSize_ > 0){notFull_.notify();}}return task;
}

这里主要关注一个虚假唤醒的问题:

while (queue_.empty() && running_)
{notEmpty_.wait();
}

关于虚假唤醒,可以查看:muduo网络库——条件变量(condition variable)_却道天凉_好个秋的博客-CSDN博客

多个线程在同时等待queue不为空的条件发生。当有信号signalA通知queue不为空时,操作系统同时唤醒所有线程,如线程A竞争成功,执行下面的pop操作,使得queue再次为空。但其他线程此时也被唤醒,又由于线程A的操作导致queue再次为空,其他线程无法执行对应的操作,即被虚假唤醒。

6)子线程运行函数

void ThreadPool::runInThread()
{try{if (threadInitCallback_){threadInitCallback_();}while (running_)        // 每一个线程都是一个loop 只有在stop的时候才会停止{Task task(take());    // 从队列中取任务 if (task)          {task();             // 如果取到任务,则执行任务内容}}}...
}

测试

#include "muduo/base/ThreadPool.h"
#include "muduo/base/CountDownLatch.h"
#include "muduo/base/CurrentThread.h"
#include "muduo/base/Logging.h"#include <stdio.h>
#include <unistd.h>// 作为入队任务,有任务执行时打印当前线程id
void print()
{printf("tid=%d\n", muduo::CurrentThread::tid());
}void test(int maxSize)
{LOG_WARN << "Test ThreadPool with max queue size = " << maxSize;muduo::ThreadPool pool("MainThreadPool");   // 创建线程池pool.setMaxQueueSize(maxSize);              // 设置队列最大值pool.start(5);                              // 开启5个子线程来进行调度LOG_WARN << "Adding";pool.run(print);  // 入队pool.run(print);  // 入队for (int i = 0; i < 100; ++i){char buf[32];snprintf(buf, sizeof buf, "task %d", i);pool.run(std::bind(printString, std::string(buf)));}LOG_WARN << "Done";muduo::CountDownLatch latch(1);pool.run(std::bind(&muduo::CountDownLatch::countDown, &latch));latch.wait();pool.stop();
}int main()
{test(0);test(1);test(5);test(10);test(50);
}

总结

线程池为避免频繁创建、销毁线程,提供一组子线程,能从工作队列取任务、执行任务,而用户可以向工作队列加入任务,从而完成用户任务。

主要步骤如下:

1)创建一组线程,每个线程为一个loop循环;

2)有任务需要执行时,入队列;

3)当有线程池中有线程空闲时,从队列中取出任务执行回调;


http://chatgpt.dhexx.cn/article/56dKmLdx.shtml

相关文章

muduo网络库——Channel

模型 实现流程&#xff1a; 前面已经介绍了EPoller类&#xff0c;EPoller主要监听的是Channel对象&#xff0c;每一个Channel对象会绑定一个文件描述符&#xff08;fd_&#xff09;&#xff0c;fd_上绑定要监听的事件。当epoll监听到就绪事件时&#xff0c;会将就绪事件添加到…

muduo源码分析之Buffer

这一次我们来分析下muduo中Buffer的作用&#xff0c;我们知道&#xff0c;当我们客户端向服务器发送数据时候&#xff0c;服务器就会读取我们发送的数据&#xff0c;然后进行一系列处理&#xff0c;然后再发送到其他地方&#xff0c;在这里我们想象一下最简单的EchoServer服务器…

从实例看muduo网络库各模块交互过程

文章目录 muduo网络库的核心代码模块各模块功能解释ChannelPollerEpollPoller EventLoopEventLoopThreadEventLoopThreadPoolTcpServerTcpConnection 从实际应用出发 muduo网络库的核心代码模块 1、channel 2、Poller 和它的子类 EpollPoller 3、EventLoop 4、Thread、EventLo…

muduo总结

本文重点在muduo TcpServer的启动&#xff0c;I/O线程池的启动&#xff0c;以及各种回调 文章目录 baseAsyncLogging.{h,cc}Atomic.hBlockinQueue.hBoundedBlockinQueue.hCondition.hcopyable.hCountDownLatch.{h,cc}Date.{h,cc}Exception.{h,cc}Logging.{h,cc}Mutex.hProcess…

muduo网络库——日志处理

测试程序 #include "muduo/base/AsyncLogging.h" #include "muduo/base/Logging.h" #include "muduo/base/Timestamp.h"#include <stdio.h> #include <sys/resource.h> #include <unistd.h>off_t kRollSize 500*1000*1000;m…

Muduo日志模块详解

Muduo日志模块解析 图片取自muduo网络库源码解析(1):多线程异步日志库(上)_李兆龙的技术博客_51CTO博客也是很好的日志讲解博客,这篇讲解流程基本上和它差不多,并且写的比我条理清楚很多 AppendFile::append() 这个函数是日志写入文件的最终函数,并且AppendFile这个类里面也是…

Muduo 定时器

TimeQueue定时器 图片转载自:muduo网络库源码解析(4):TimerQueue定时机制_李兆龙的技术博客_51CTO博客 添加新的定时器 TimerId TimerQueue::addTimer(TimerCallback cb, //用户自定义回调Timestamp when, //定时器的超时时刻double interval) //重复触发间隔,小于0则不重…

《muduo网络库》学习笔记——muduo学习总结

muduo是基于非阻塞的IO和事件驱动的网络库&#xff08;Reactor模式&#xff09;&#xff0c;其核心是一个事件循环EventLoop&#xff0c;用于响应计时器和IO事件。muduo采用基于对象&#xff08;object-based&#xff09;而非面向对象&#xff08;object-oriented&#xff09;的…

Ubuntu安装muduo库

1. 首先安装boost库&#xff1b; sudo apt-get update sudo apt-get install libboost-all-dev 2. 下载muduo库&#xff0c; https://github.com/chenshuo/muduo 3. 解压后进入解压目录&#xff0c;vim CMakeLists.txt&#xff0c;注释掉略过unit_test测试用例代码的编译&#…

linux muduo 编译安装,muduo记录

1.muduo编译安装 编译muduo遇见的报错可以在github上的issue上面查找。一般都能顺利解决,我遇到的就是没有安装boost-dev. centos7系统 执行: sudo yum install boost-dev 2.截取流程图 图片截取自《Linux多线程服务端编程&#xff1a;使用muduo C网络库》 3.源码摘录 摘录一个…

muduo源码分析之TcpServer模块

这次我们开始muduo源代码的实际编写&#xff0c;首先我们知道muduo是LT模式&#xff0c;Reactor模式&#xff0c;下图为Reactor模式的流程图[来源1] 然后我们来看下muduo的整体架构[来源1] 首先muduo有一个主反应堆mainReactor以及几个子反应堆subReactor&#xff0c;其中子反应…

muduo网络库学习(1)

muduo网络库学习&#xff08;1&#xff09; 文章目录 muduo网络库学习&#xff08;1&#xff09;前言一、muduo是什么&#xff1f;二、代码结构1.base库2.net库3.附属库 二、网络库结构总结 前言 本章节主要介绍muduo网络库的整体架构&#xff01;一、muduo是什么&#xff1f;…

muduo

muduo 概述 muduo是基于Reactor模式的网络库&#xff0c;用于响应计时器和IO事件。 muduo采用基于对象而非面向对象的设计风格&#xff0c;其事件回调采用functionbind&#xff0c;用户在使用muduo的时候不需要继承其中的class 架构 Multiple Reactor Reactor模式&#xff1a…

muduo日志库原理以及源码分析

muduo日志库特点 日志批量写入批量唤醒写线程写日志用notifywait_timeout 方式触发日志的写入锁的粒度&#xff0c;双缓冲&#xff0c;双队列buffer默认 4M 缓冲区&#xff0c; buffers 是 buffer 队列&#xff0c; push 、 pop 时使用 move 语义 减少内存拷贝 muduo的这些特点…

muduo网络库与服务模型介绍

目录 一、muduo网络库简介 1、特点 2、代码结构 &#xff08;1&#xff09;公共接口 &#xff08;2&#xff09;内部实现 二、muduo线程模型 1、单线程Reactor 2、Reactor线程池 3、one loop per thread 4、one loop per thread 线程池 muduo是陈硕个人使用C开发的一…

muduo 架构解析

muduo是一个基于Reactor模式的C网络库。它采用非阻塞I/O模型&#xff0c;基于事件驱动和回调。我们不仅可以通过muduo来学习linux服务端多线程编程&#xff0c;还可以通过它来学习C11。     Reactor是网络编程的一般范式。我们这里从reactor模式为出发点&#xff0c;根据R…

muduo库介绍

muduo库是一个多线程服务器开发库 muduo 作者陈硕&#xff0c;现在在美国加州硅谷某互联网大公司工作&#xff0c;从事大规模分布式的可靠系统工程。这个库是作者多年工作的总结&#xff0c;可以说大家学通了这个库&#xff0c;找一份Linux服务器开发的工作是没问题的&#xf…

C++ muduo网络库知识分享01 - Linux平台下muduo网络库源码编译安装

Muduo is a multithreaded C network library based on the reactor pattern. muduo库的介绍就是&#xff1a;一个基于reactor反应堆模型的多线程C网络库。 muduo网络库是C语言开发的一个非常优秀的网络库&#xff0c;作者陈硕&#xff0c;muduo网络库在多线程环境下性能非常高…

遗传算法示例

遗传的概念&#xff1a; 遗传算法是模拟达尔文生物进化论的自然选择和遗传学机理的生物进化过程的计算模型&#xff0c;是一种通过模拟自然进化过程搜索最优解的方法。 遗传算法的特点&#xff1a; 对于搜索算法的共同特征有 首先组成一组候选解。依据某些使用性条件测算这些…

10分钟搞懂遗传算法

大自然有种神奇的力量&#xff0c;它能够将优良的基因保留下来&#xff0c;从而进化出更加强大、更加适合生存的基因。遗传算法便基于达尔文的进化论&#xff0c;模拟了自然选择&#xff0c;物竞天择、适者生存&#xff0c;通过N代的遗传、变异、交叉、复制&#xff0c;进化出问…