📢博客主页:https://blog.csdn.net/2301_779549673
📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson
📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
📢本文由 JohnKi 原创,首发于 CSDN🙉
📢未来很长,值得我们全力奔赴更美好的生活✨


文章目录
- 📢前言
- 🏳️🌈一、什么是线程池
- 🏳️🌈二、线程池逻辑框架
- 🏳️🌈三、核心接口说明
-
- [3.1 构造函数 ThreadPool(int num)](#3.1 构造函数 ThreadPool(int num))
- [3.2 任务提交 Equeue(T &&in)](#3.2 任务提交 Equeue(T &&in))
- [3.3 启动线程池 Start()](#3.3 启动线程池 Start())
- [3.4 停止线程池 Stop()](#3.4 停止线程池 Stop())
- [3.5 等待线程结束 Wait()](#3.5 等待线程结束 Wait())
- [3.6 线程主逻辑 HandlerTask(std::string name)](#3.6 线程主逻辑 HandlerTask(std::string name))
- 🏳️🌈四、整体代码
- 👥总结
📢前言
上文中,笔者带领大家实现了 日志类 ,现在我们已经将 线程 、锁 、条件 、日志这些线程池中都会用到的基本类都模拟实现过一遍了。
线程池 是 一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。
现在我们将这些结合起来,深度了解一下线程池,并模拟实现一下
🏳️🌈一、什么是线程池
线程池的意义
提高线程复用率,避免频繁创建/销毁线程的开销
统一管理任务调度,提升系统稳定性
线程池的应用场景:
- 需要大量的线程来完成任务,且完成任务的时间比较短。比如WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。
- 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。
- 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,出现错误.
线程池的种类
创建固定数量线程池,循环从任务队列中获取任务对象,获取到任务对象后,执行任务对象中
- 的任务接口
- 浮动线程池,其他同上
此处,我们选择固定线程个数的线程池。

🏳️🌈二、线程池逻辑框架
整体架构
这是一个 通用线程池模板类,基于 生产者-消费者模型,实现多线程任务的异步调度与执行。核心组件包括:
- 任务队列:存储待处理任务,支持线程安全操作。
- 工作线程组:从队列中获取任务并执行。
- 同步机制:互斥锁(Mutex)与条件变量(Cond)确保线程安全。
bash
namespace ThreadPoolModule {
using namespace LogModule;
using namespace ThreadModule;
using namespace LockModule;
using namespace CondModule;
using thread_t = std::shared_ptr<Thread>;
const static int defaultnum = 5;
template <typename T> class ThreadPool {
private:
// 判断线程池是否为空
bool IsEmpty() { return _taskq.empty(); }
// 工作线程的主循环逻辑
void HandlerTask(std::string name);
public:
ThreadPool(int num = defaultnum)
: _num(num), _wait_num(0), _isrunning(false);
void Equeue(T&& in); // 任务入队
void Start(); // 启动线程池
void Wait(); // 等待线程池结束
void Stop(); // 停止线程池
~ThreadPool(); // 析构函数
private:
std::vector<thread_t> _threads;
int _num;
int _wait_num;
std::queue<T> _taskq; // 临界资源
Mutex _lock;
Cond _cond;
bool _isrunning;
};
} // namespace ThreadPoolModule
成员变量解析
🏳️🌈三、核心接口说明
3.1 构造函数 ThreadPool(int num)
功能 :初始化线程池,创建工作线程对象(未启动)。
流程:
- 设置线程数量 _num。
- 循环创建 num 个线程对象,绑定到 HandlerTask 方法。
- 记录日志(如 构建线程Thread-1对象 ... 成功)。
bash
ThreadPool(int num = defaultnum)
: _numThreads(num), _wait_nums(0), _isrunning(false) {
for (int i = 0; i < _numThreads; ++i) {
_threads.push_back(std::make_shared<Thread>(
std::bind(&ThreadPool::HandlerTask, this, std::placeholders::_1)));
LOG(LogLevel::INFO)
<< "构建线程" << _threads.back()->Name() << "对象...成功";
}
}
3.2 任务提交 Equeue(T &&in)
功能 :向任务队列添加新任务。
参数 :T &&in(右值引用,支持移动语义优化性能)。
流程:
- 加锁检查线程池是否运行(if (!_isrunning) return;)。
- 将任务推入队列(_taskq.push(std::move(in)))。
- 若有等待线程(_wait_num > 0),唤醒一个线程(_cond.Notify())。
bash
// 任务入队
void Equeue(T&& in) {
LockGuard lock(_mutex);
if (!_isrunning)
_taskq.push(std::move(
in)); // 将任务对象 in 高效地添加到任务队列 _taskq
if (_wait_nums > 0)
_cond.Notify();
}
3.3 启动线程池 Start()
功能 :启动所有工作线程。
流程:
- 检查是否已运行(避免重复启动)。
- 设置 _isrunning = true。
- 遍历线程对象,调用 Start() 启动线程,记录日志。
bash
// 启动所有线程池
void Start() {
if (_isrunning)
return;
_isrunning = true;
for (auto& thread_ptr : _threads) {
LOG(LogLevel::INFO) << "启动线程" << thread_ptr->Name() << "...成功";
thread_ptr->Start();
}
}
3.4 停止线程池 Stop()
功能 :停止接收新任务,唤醒所有线程处理剩余任务后退出。
流程:
- 加锁设置 _isrunning = false。
- 若有等待线程(_wait_num > 0),唤醒所有线程(_cond.NotifyAll())。
bash
// 通知所有线程退出。
void Stop() {
LockGuard lock(_mutex);
if (_isrunning) {
_isrunning = false;
if (_wait_nums > 0)
_cond.NotifyAll();
}
}
3.5 等待线程结束 Wait()
功能 :阻塞等待所有工作线程完成任务并退出。
流程:
- 遍历线程对象,调用 Join() 等待线程结束,记录日志。
bash
// 阻塞等待所有工作线程退出
void Wait() {
for (auto& thread_ptr : _threads) {
thread_ptr->Join();
LOG(LogLevel::INFO) << "回收线程" << thread_ptr->Name() << "...成功";
}
}
3.6 线程主逻辑 HandlerTask(std::string name)
功能 :工作线程的核心循环逻辑。
流程:
- 循环获取任务:
- 加锁检查任务队列是否为空且线程池运行中。
- 若队列空且运行中,线程进入等待(_cond.Wait(_lock))。
- 若队列空且线程池已停止,退出循环。
- 取出队列头部任务(t = _taskq.front(); _taskq.pop();)。
- 执行任务(t(name),要求任务类型 T 实现 operator())。
- 线程退出时记录日志。
bash
// 工作线程的主循环逻辑
void HandlerTask(std::string name) {
LOG(LogLevel::INFO) << "线程:" << name << ",进入HandlerTask的逻辑";
while (true) {
T t;
{
LockGuard lock(_mutex);
while (IsEmpty() && _isrunning) {
_wait_nums++;
_cond.Wait(_mutex);
_wait_nums--;
}
// 任务队列为空 && 线程池退出了
if (IsEmpty() && !_isrunning)
break;
// 取出任务
t = _taskq.front();
_taskq.pop();
}
// 模拟处理任务
t(name);
}
LOG(LogLevel::INFO) << "线程:" << name << ",退出";
}
🏳️🌈四、整体代码
这下面是 ThreadPool.hpp 部分的代码,有需要其他部分的代码,可以去笔者的仓库中获取
https://gitee.com/JohnKingW/linux_test/tree/master/lesson/CSDN_code/7.ThreadPool
bash
#pragma once
#include <queue>
#include "Log.hpp"
#include "Mutex.hpp"
#include "Cond.hpp"
#include "Thread.hpp"
namespace ThreadPoolModule{
using namespace LogModule;
using namespace ThreadModule;
using namespace LockModule;
using namespace CondModule;
using thread_t = std::shared_ptr<Thread>;
const static int defaultnum = 5;
template <typename T>
class ThreadPool{
public:
// 构造函数
ThreadPool(int num = defaultnum)
: _numThreads(num),
_wait_nums(0),
_isrunning(false)
{
for(int i = 0; i < _numThreads; ++i){
_threads.push_back(std::make_shared<Thread>(std::bind(&ThreadPool::HandlerTask, this, std::placeholders::_1)));
LOG(LogLevel::INFO) << "构建线程" << _threads.back()->Name() << "对象...成功";
}
}
// 任务入队
void Equeue(T&& in){
LockGuard lock(_mutex);
if(!_isrunning)
_taskq.push(std::move(in)); // 将任务对象 in 高效地添加到任务队列 _taskq
if(_wait_nums > 0)
_cond.Notify();
}
// 启动所有线程池
void Start(){
if(_isrunning) return;
_isrunning = true;
for(auto& thread_ptr : _threads){
LOG(LogLevel::INFO) << "启动线程" << thread_ptr->Name() << "...成功";
thread_ptr->Start();
}
}
// 阻塞等待所有工作线程退出
void Wait(){
for(auto& thread_ptr : _threads){
thread_ptr->Join();
LOG(LogLevel::INFO) << "回收线程" << thread_ptr->Name() << "...成功";
}
}
// 通知所有线程退出。
void Stop(){
LockGuard lock(_mutex);
if(_isrunning){
_isrunning = false;
if(_wait_nums>0)
_cond.NotifyAll();
}
}
// 析构函数
~ThreadPool(){}
private:
// 判断线程池是否为空
bool IsEmpty() { return _taskq.empty(); }
// 工作线程的主循环逻辑
void HandlerTask(std::string name){
LOG(LogLevel::INFO) << "线程:" << name << ",进入HandlerTask的逻辑";
while(true){
T t;
{
LockGuard lock(_mutex);
while(IsEmpty() && _isrunning){
_wait_nums++;
_cond.Wait(_mutex);
_wait_nums--;
}
// 任务队列为空 && 线程池退出了
if(IsEmpty() && !_isrunning)
break;
// 取出任务
t = _taskq.front();
_taskq.pop();
}
// 模拟处理任务
t(name);
}
LOG(LogLevel::INFO) << "线程:" << name << ",退出";
}
private:
std::vector<thread_t> _threads; // 线程池
int _numThreads; // 线程池中线程的数量
int _wait_nums; // 等待线程的数量
std::queue<T> _taskq; // 临界资源,生产者资源剩余量
Mutex _mutex; // 互斥锁
Cond _cond; // 条件变量
bool _isrunning; // 线程池是否正在运行
};
}

👥总结
本篇博文对 【Linux】线程池详解及基本实现 做了一个较为详细的介绍,不知道对你有没有帮助呢
觉得博主写得还不错的三连支持下吧!会继续努力的~
