> 技术文档 > 【Linux】线程池设计 + 策略模式_线程池策略

【Linux】线程池设计 + 策略模式_线程池策略

🌈 个人主页:Zfox_
🔥 系列专栏:Linux

【Linux】线程池设计 + 策略模式_线程池策略

目录

  • 一:🔥 线程
    • 🦋 1-1 ⽇志与策略模式
    • 🎀 策略模式
    • 🦋 1-2 线程池设计
    • 🦋 1-3 线程安全的单例模式
      • 1-3-1 什么是单例模式
      • 1-3-2 单例模式的特点
      • 1-3-3 饿汉实现⽅式和懒汉实现⽅式
      • 1-3-4 饿汉⽅式实现单例模式
      • 1-3-5 懒汉⽅式实现单例模式
      • 1-3-6 懒汉⽅式实现单例模式(线程安全版本)
    • 🦋 1-4 单例式线程池
  • 二:🔥 共勉

一:🔥 线程池

🌶️ 下⾯开始,我们结合我们之前所做的所有封装,进⾏⼀个线程池的设计。在写之前,我们要做如下准备

  • 准备线程的封装
  • 准备锁和条件变量的封装
  • 引⼊⽇志,对线程进⾏封装
    这里用到了我们上一篇博客用到的头文件及代码

🦋 1-1 ⽇志与策略模式

  • 🍧 什么是设计模式
    IT⾏业这么⽕, 涌⼊的⼈很多. 俗话说林⼦⼤了啥⻦都有. ⼤佬和菜鸡们两极分化的越来越严重. 为了让菜鸡们不太拖⼤佬的后腿, 于是⼤佬们针对⼀些经典的常⻅的场景, 给定了⼀些对应的解决⽅案, 这个就是 设计模式
  • 🍧 ⽇志认识
    计算机中的⽇志是记录系统和软件运⾏中发⽣事件的⽂件,主要作⽤是监控运⾏状态、记录异常信息,帮助快速定位问题并⽀持程序员进⾏问题修复。它是系统维护、故障排查和安全管理的重要⼯具。

🍡 ⽇志格式以下⼏个指标是必须得有的:

  • 时间戳
  • ⽇志等级
  • ⽇志内容

🍡 以下⼏个指标是可选的

  • ⽂件名⾏号
  • 进程,线程相关id信息等

⽇志有现成的解决⽅案,如:spdlog、glog、Boost.Log、Log4cxx等等,我们依旧采⽤⾃定义⽇志的⽅式。

这⾥我们采⽤ 设计模式-策略模式 来进⾏⽇志的设计。
我们想要的⽇志格式如下:

[可读性很好的时间] [⽇志等级] [进程pid] [打印对应⽇志的⽂件名][⾏号] - 消息内容,⽀持可变参数[2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [16] - hello world[2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [17] - hello world[2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [18] - hello world[2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [20] - hello world[2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [21] - hello world[2024-08-04 12:27:03] [WARNING] [202938] [main.cc] [23] - hello world

模式讲解详见代码注释
Log.hpp

#pragma once#include #include #include #include #include #include #include  // c++17#include #include #include \"Mutex.hpp\"namespace LogModule{ using namespace LockModule; // 获取一下当前系统的时间 std::string CurrentTime() { time_t time_stamp = ::time(nullptr); struct tm curr; localtime_r(&time_stamp, &curr); // 时间戳,获取可读性较强的时间信息S char buffer[1024]; // bug snprintf(buffer, sizeof(buffer), \"%4d-%02d-%02d %02d:%02d:%02d\", curr.tm_year + 1900, curr.tm_mon + 1, curr.tm_mday, curr.tm_hour, curr.tm_min, curr.tm_sec ); return buffer; } // 构成:1. 构建日志字符串 2. 刷新落盘(screen, file) // 1. 日志文件的默认路径和文件名 const std::string defaultlogpath = \"./log/\"; const std::string defaultlogname = \"log.txt\"; // 2. 日志等级 enum class LogLevel { DEBUG = 1, INFO, WARNING, ERROR, FATAL }; std::string Level2String(LogLevel level) { switch(level) { case LogLevel::DEBUG: return \"DEBUG\"; case LogLevel::INFO: return \"INFO\"; case LogLevel::WARNING: return \"WARNING\"; case LogLevel::ERROR: return \"ERROR\"; case LogLevel::FATAL: return \"FATAL\"; default: return \"None\"; } } // 3. 刷新策略 class LogStrategy { public: virtual ~LogStrategy() = default; virtual void SyncLog(const std::string &message) = 0; }; // 3.1 控制台策略 class ConsoleLogStrategy : public LogStrategy { public: ConsoleLogStrategy() {} ~ConsoleLogStrategy() {} void SyncLog(const std::string &message) { LockGuard lockguard(_lock); std::cout << message << std::endl; } private: Mutex _lock; }; // 3.2 文件级(磁盘)策略 class FileLogStrategy : public LogStrategy { public: FileLogStrategy(const std::string &logpath = defaultlogpath, const std::string &logname = defaultlogname) :_logpath(logpath) ,_logname(logname) { // 确认_logpath是存在的 LockGuard lockguard(_lock); if(std::filesystem::exists(_logpath)) { return ; } try { std::filesystem::create_directories(_logpath); } catch(const std::filesystem::filesystem_error& e) { std::cerr << e.what() << \'\\n\'; }  } ~FileLogStrategy() {} void SyncLog(const std::string &message) { LockGuard lockguard(_lock); std::string log = _logpath + _logname; // ./log/log.txt std::ofstream out(log, std::ios::app); // 日志写入,一定是追加 if(!out.is_open()) { return ; } out << message << \'\\n\'; out.close(); } private: std::string _logpath; std::string _logname; Mutex _lock; }; // 日志类:构建日志字符串,根据策略,进行刷新 class Logger { public: Logger() { // 默认采用ConsoleLogStrategy策略 _strategy = std::make_shared<ConsoleLogStrategy>(); } void EnableConsoleLog() { _strategy = std::make_shared<ConsoleLogStrategy>(); } void EnableFileLog() { _strategy = std::make_shared<FileLogStrategy>(); } ~Logger() {} // 一条完整的信息:[2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [16] + 日志的可变部分(<< \"hello world\" << 3.14 << a << b;) class LogMessage { public: LogMessage(LogLevel level, const std::string &filename, int line, Logger &logger) :_currtime(CurrentTime()) ,_level(level) ,_pid(::getpid()) ,_filename(filename) ,_line(line) ,_logger(logger) { std::stringstream ssbuffer; ssbuffer << \"[\" << _currtime << \"] \" << \"[\" << Level2String(_level) << \"] \" << \"[\" << _pid << \"] \" << \"[\" << _filename << \"] \" << \"[\" << _line << \"] - \"; _loginfo = ssbuffer.str(); } template<typename T> LogMessage &operator << (const T &info) { std::stringstream ss; ss << info; _loginfo += ss.str(); return *this; } ~LogMessage() { if(_logger._strategy) {  _logger._strategy->SyncLog(_loginfo); } } private: std::string _currtime; // 当前日志的时间 LogLevel _level; // 日志等级 pid_t _pid;  // 进程pid std::string _filename; // 源文件名称?? int _line;  // 日志所在的行号 Logger &_logger; // 负责根据不同的策略进行刷新 std::string _loginfo; // 一条完整的日志记录 }; // 就是要拷贝 LogMessage operator()(LogLevel level, const std::string &filename, int line) { return LogMessage(level, filename, line, *this); // 优化成一次构造一次析构了 连续的构造 + 拷贝构造 } private: std::shared_ptr<LogStrategy> _strategy; // 日志刷新的策略方案 }; Logger logger;#define LOG(Level) logger(Level, __FILE__, __LINE__)#define ENABLE_CONSOLE_LOG() logger.EnableConsoleLog()#define ENABLE_FILE_LOG() logger.EnableFileLog()}

🥗 使⽤样例:

#include \"Log.hpp\"using namespace LogModule;int main(){ ENABLE_FILE_LOG(); LOG(LogLevel::DEBUG) << \"hello file\"; LOG(LogLevel::DEBUG) << \"hello file\"; LOG(LogLevel::DEBUG) << \"hello file\"; LOG(LogLevel::DEBUG) << \"hello file\"; ENABLE_CONSOLE_LOG(); LOG(LogLevel::DEBUG) << \"hello world\"; LOG(LogLevel::DEBUG) << \"hello world\"; LOG(LogLevel::DEBUG) << \"hello world\"; LOG(LogLevel::DEBUG) << \"hello world\"; return 0;}

🎀 策略模式

策略模式是很常用的设计模式,一般用于当一个功能存在多种算法时, 需要根据不同的情况使用不同的计算算法, 而策略模式完美适配这种场景。

📚 策略模式的特点:

  1. 算法封装将算法的实现与使用算法的代码分离,通过封装提高代码的灵活性和可扩展性。
  2. 动态替换可以在运行时选择和替换算法。(unique_ptr)
  3. 遵循开闭原则新增策略无需修改现有代码。

📚 典型场景:

  • 支付系统支持多种支付方式(如微信,支付宝,信用卡)。
  • 数据压缩提供不同的压缩算法。
  • 日至策略根据日志级别动态选择记录策略。(本博客是控制台策略和文件策略)

📚 策略模式的组成:

  1. 策略接口(Strategy)定义算法的通用接口。
  2. 具体策略(ConcreteStrategy):实现具体的算法。
  3. 上下文类(Context):持有策略接口的引用,调用具体策略的方法。

策略模式的优缺点

📚 优点:

  • 提供了算法的灵活选择和切换。
  • 提高了代码的可读性和可维护性。
  • 遵循了开闭原则(对扩展开放,对修改关闭)。

📚 缺点:

  • 可能需要定义大量的策略类。
  • 客户端需要了解所有策略的功能,并选择合适的策略。

🦋 1-2 线程池设计

线程池:

  • 线程池通过一个线程安全的阻塞任务队列加上一个或一个以上的线程实现,线程池中的线程可以从阻塞队列中获取任务进行任务处理,当线程都处于繁忙状态时可以将任务加入阻塞队列中,等到其它的线程空闲后进行处理。
  • 可以避免大量线程频繁创建或销毁所带来的时间成本,也可以避免在峰值压力下,系统资源耗尽的风险;并且可以统一对线程池中的线程进行管理,调度监控。

💜 线程池的应⽤场景:

  • 🧁 需要⼤量的线程来完成任务,且完成任务的时间⽐较短。 ⽐如WEB服务器完成⽹⻚请求这样的任务,使⽤线程池技术是⾮常合适的。因为单个任务⼩,⽽任务数量巨⼤,你可以想象⼀个热⻔⽹站的点击次数。 但对于⻓时间的任务,⽐如⼀个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间⽐线程的创建时间⼤多了。
  • 🧁 对性能要求苛刻的应⽤,⽐如要求服务器迅速响应客⼾请求。
  • 🧁 接受突发性的⼤量请求,但不⾄于使服务器因此产⽣⼤量线程的应⽤。突发性⼤量客⼾请求,在没有线程池情况下,将产⽣⼤量线程,虽然理论上⼤部分操作系统线程数⽬最⼤值不是问题,短时间内产⽣⼤量线程可能使内存到达极限,出现错误。

🌶️ 线程池的种类

  • 创建固定数量线程池,循环从任务队列中获取任务对象,获取到任务对象后,执⾏任务对象中的任务接⼝
  • 浮动线程池,其他同上,此处,我们选择固定线程个数的线程池。

【Linux】线程池设计 + 策略模式_线程池策略
ThreadPool.hpp

#pragma once#include #include #include #include #include #include \"Log.hpp\"#include \"Mutex.hpp\"#include \"Cond.hpp\"#include \"Thread.hpp\"namespace ThreadPoolModule{ using namespace LogMudule; using namespace ThreadModule; using namespace LockModule; using namespace CondModule; // 用来做测试的线程方法 void DefaultTest() { while (true) { LOG(LogLevel::DEBUG) << \"我是一个测试方法\"; sleep(1); } } 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) { LOG(LogLevel::INFO) << \"线程: \" << name << \", 进入HandlerTask的逻辑\"; while (true) { // 1. 拿任务 T t; {  LockGuard lockguard(_lock);  while (IsEmpty() && _isrunning)  { _wait_num++; _cond.Wait(_lock); _wait_num--;  }  // 2. 任务队列为空 && 线程池退出了  if(IsEmpty() && !_isrunning) break;  t = _taskq.front();  _taskq.pop(); } // 2. 处理任务 t(name); // 规定,未来所有的任务处理,全部都是必须提供()方法! } LOG(LogLevel::INFO) << \"线程: \" << name << \" 退出\"; } public: ThreadPool(int num = defaultnum) : _num(num), _wait_num(0), _isrunning(false) { for (int i = 0; i < _num; 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 lockguard(_lock); if(!_isrunning) return; _taskq.push(std::move(in)); if(_wait_num > 0) _cond.Notify(); } void Start() { if(_isrunning) return; _isrunning = true; // bug fix?? 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 lockguard(_lock); if(_isrunning) { // 3. 不能在入任务了 _isrunning = false; // 不工作 // 1. 让线程自己退出(要唤醒) && // 2. 历史的任务被处理完了 if(_wait_num>0)  _cond.NotifyAll(); } } ~ThreadPool() { } private: std::vector<thread_t> _threads; int _num; int _wait_num; std::queue<T> _taskq; // 临界资源 Mutex _lock; Cond _cond; bool _isrunning; };}

Task.hpp

#pragma#include #include #include \"Log.hpp\"using namespace LogModule;using task_t = std::function<void(std::string name)>;void Push(std::string name){ LOG(LogLevel::DEBUG) << \"我是一个将数据推送到服务器的任务,正在被执行\" << \"[\" << name << \"]\";}

main.cc

#include \"ThreadPool.hpp\"#include \"Task.hpp\"#include using namespace ThreadPoolModule;int main(){ ENABLE_FILE_LOG(); std::unique_ptr<ThreadPool<task_t>> tp = std::make_unique<ThreadPool<task_t>>(); tp->Start(); int cnt = 10; while(cnt) { tp->Equeue(Push); cnt--; sleep(1); } tp->Stop(); sleep(3); tp->Wait(); return 0;}
g++ main.cc -std=c++17 -lpthread // 需要使⽤C++17

运行结果:

$ ./a.out[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [62] - ThreadPoolConstruct()[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [70] - init threadThread-0 done[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [70] - init threadThread-1 done[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [70] - init threadThread-2 done[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [70] - init threadThread-3 done[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [70] - init threadThread-4 done[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [79] - start threadThread-0done[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [79] - start threadThread-1done[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [28] - Thread-0 isrunning...[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [79] - start threadThread-2done[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [79] - start threadThread-3done[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [28] - Thread-3 isrunning...[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [28] - Thread-2 isrunning...[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [79] - start threadThread-4done[2024-08-04 15:09:29] [DEBUG] [206342] [ThreadPool.hpp] [109] - 任务⼊队列成功[2024-08-04 15:09:29] [DEBUG] [206342] [ThreadPool.hpp] [52] - Thread-0 get ataskthis is a task[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [28] - Thread-1 isrunning...[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [28] - Thread-4 isrunning...[2024-08-04 15:09:30] [DEBUG] [206342] [ThreadPool.hpp] [109] - 任务⼊队列成功[2024-08-04 15:09:30] [DEBUG] [206342] [ThreadPool.hpp] [52] - Thread-3 get ataskthis is a task...this is a task[2024-08-04 15:09:39] [DEBUG] [206342] [ThreadPool.hpp] [88] - 线程池退出中...[2024-08-04 15:09:44] [INFO] [206342] [ThreadPool.hpp] [95] - Thread-0 退出...[2024-08-04 15:09:44] [INFO] [206342] [ThreadPool.hpp] [95] - Thread-1 退出...[2024-08-04 15:09:44] [INFO] [206342] [ThreadPool.hpp] [95] - Thread-2 退出...[2024-08-04 15:09:44] [INFO] [206342] [ThreadPool.hpp] [95] - Thread-3 退出...[2024-08-04 15:09:44] [INFO] [206342] [ThreadPool.hpp] [95] - Thread-4 退出..

🦋 1-3 线程安全的单例模式

1-3-1 什么是单例模式

单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供全局访问点来访问这个实例。在C++中,单例模式通常用于需要控制资源访问或管理全局状态的情况下,比如日志记录器、配置管理器、线程池等。

1-3-2 单例模式的特点

某些类, 只应该具有⼀个对象(实例), 就称之为单例. 例如⼀个男⼈只能有⼀个媳妇.

在很多服务器开发场景中, 经常需要让服务器加载很多的数据 (上百G) 到内存中. 此时往往要⽤⼀个单例
的类来管理这些数据.

1-3-3 饿汉实现⽅式和懒汉实现⽅式

🍱 [洗碗的例⼦]

吃完饭, ⽴刻洗碗, 这种就是饿汉⽅式. 因为下⼀顿吃的时候可以⽴刻拿着碗就能吃饭.吃完饭, 先把碗放下, 然后下⼀顿饭⽤到这个碗了再洗碗, 就是懒汉⽅式.

懒汉⽅式最核⼼的思想是 “延时加载”. 从⽽能够优化服务器的启动速度.

1-3-4 饿汉⽅式实现单例模式

template <typename T>class Singleton {static T data;public:static T* GetInstance() {return &data;}};

🍱 - 只要通过 Singleton 这个包装类来使⽤ T 对象, 则⼀个进程中只有⼀个 T 对象的实例。

1-3-5 懒汉⽅式实现单例模式

template <typename T>class Singleton {static T* inst;public:static T* GetInstance() {if (inst == NULL) {inst = new T();} return inst;}};

存在⼀个严重的问题, 线程不安全.
第⼀次调⽤ GetInstance 的时候, 如果两个线程同时调⽤, 可能会创建出两份 T 对象的实例.
但是后续再次调⽤, 就没有问题了.

1-3-6 懒汉⽅式实现单例模式(线程安全版本)

// 懒汉模式, 线程安全template <typename T>class Singleton {volatile static T* inst; // 需要设置 volatile 关键字, 否则可能被编译器优化.static std::mutex lock;public:static T* GetInstance() {if (inst == NULL) { // 双重判定空指针, 降低锁冲突的概率, 提⾼性能.lock.lock(); // 使⽤互斥锁, 保证多线程情况下也只调⽤⼀次 new.if (inst == NULL) {inst = new T();} lock.unlock();} return inst;}}

注意事项:

  1. 加锁解锁的位置
  2. 双重 if 判定, 避免不必要的锁竞争
  3. volatile关键字防⽌过度优化 (指令重排序和从寄存器中读取数据) (可见性和有序性)

🦋 1-4 单例式线程池

ThreadPool.hpp

#pragma once#include #include #include #include #include #include \"Mutex.hpp\"#include \"Log.hpp\"#include \"Cond.hpp\"#include \"Thread.hpp\"namespace ThreadPoolModule{ using namespace ThreadModule; using namespace LockModule; using namespace CondModule; using namespace LogModule; // 我是来做测试的线程方法 void DefaultTest() { while (true) { LOG(LogLevel::DEBUG) << \"我是一个线程方法\"; ::sleep(1); } } 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) { LOG(LogLevel::INFO) << \"线程: \" << name << \", 进入了HandletTask的执行逻辑\"; while (true) { // 1. 拿任务 T t; {  LockGuard lockguard(_lock);  while (IsEmpty() && _isrunning)  { _wait_num++; _cond.Wait(_lock); _wait_num--;  }  // 2. 任务队列为空 && 线程池退出了  if (IsEmpty() && !_isrunning) break;  t = _taskq.front();  _taskq.pop(); } // 2. 处理任务 t(name); // 规定 所有的任务处理 全部提供()方法 } LOG(LogLevel::INFO) << \"线程: \" << name << \" 退出\"; } ThreadPool(const ThreadPool<T> &) = delete; const ThreadPool<T> &operator=(const ThreadPool<T> &) = delete; ThreadPool(int num = defaultnum) : _num(num), _wait_num(0), _isrunning(false) { for (int i = 0; i < _num; i++) { _threads.push_back(std::make_shared<Thread>(std::bind(&ThreadPool::HandlerTask, this, std::placeholders::_1))); LOG(LogLevel::DEBUG) << \"构建线程\" << _threads.back()->Name() << \"对象 ... 成功\"; } } public: static ThreadPool<T> *getInstance() { if (instance == nullptr) { LockGuard lockguard(mutex); if (instance == nullptr) {  LOG(LogLevel::INFO) << \"单例首次被执行,需要加载对象...\";  instance = new ThreadPool<T>(); } } return instance; } void Equeue(T &&in) { LockGuard lockguard(_lock); if (!_isrunning) return; _taskq.push(std::move(in)); if (_wait_num > 0) _cond.Notify(); } void Start() { if (_isrunning) return; _isrunning = true; // bug fix?? for (auto &thread_ptr : _threads) { thread_ptr->Start(); LOG(LogLevel::INFO) << \"启动线程\" << thread_ptr->Name() << \" ... 成功\"; } } void Wait() { for (auto &thread_ptr : _threads) { thread_ptr->Join(); LOG(LogLevel::INFO) << \"回收线程\" << thread_ptr->Name() << \" ... 成功\"; } } void Stop() { LockGuard lockguard(_lock); if (_isrunning) { // 3. 不能再入任务了 _isrunning = false; // 不工作 // 1. 让线程自己退出(要唤醒) && 2. 历史的任务被处理完了 if (_wait_num > 0)  _cond.NotifyAll(); } } ~ThreadPool() { } private: std::vector<thread_t> _threads; int _num; int _wait_num; std::queue<T> _taskq; // 临界资源 Mutex _lock; Cond _cond; bool _isrunning; static ThreadPool<T> *instance; static Mutex mutex; // 只用来保护单例 }; template <typename T> ThreadPool<T> *ThreadPool<T>::instance = nullptr; template <typename T> Mutex ThreadPool<T>::mutex;}

测试代码

#include \"ThreadPool.hpp\"#include \"Task.hpp\"#include using namespace ThreadPoolModule;int main(){ ENABLE_CONSOLE_LOG(); ThreadPool<task_t>::getInstance()->Start(); int cnt = 10; while(cnt) { ThreadPool<task_t>::getInstance()->Equeue(Push); cnt--; sleep(1); } ThreadPool<task_t>::getInstance()->Stop(); sleep(3); ThreadPool<task_t>::getInstance()->Wait()return 0;}

运行结果:

root@hcss-ecs-a9ee:~/code/linux/112/lesson32/2.ThreadPool# ./thread_pool [2024-11-29 10:51:04] [INFO] [400187] [ThreadPool.hpp] [89] - 单例首次被执行,需要加载对象...[2024-11-29 10:51:04] [DEBUG] [400187] [ThreadPool.hpp] [77] - 构建线程Thread-1对象 ... 成功[2024-11-29 10:51:04] [DEBUG] [400187] [ThreadPool.hpp] [77] - 构建线程Thread-2对象 ... 成功[2024-11-29 10:51:04] [DEBUG] [400187] [ThreadPool.hpp] [77] - 构建线程Thread-3对象 ... 成功[2024-11-29 10:51:04] [DEBUG] [400187] [ThreadPool.hpp] [77] - 构建线程Thread-4对象 ... 成功[2024-11-29 10:51:04] [DEBUG] [400187] [ThreadPool.hpp] [77] - 构建线程Thread-5对象 ... 成功[2024-11-29 10:51:04] [INFO] [400187] [ThreadPool.hpp] [115] - 启动线程Thread-1 ... 成功[2024-11-29 10:51:04] [INFO] [400187] [ThreadPool.hpp] [115] - 启动线程Thread-2 ... 成功[2024-11-29 10:51:04] [INFO] [400187] [ThreadPool.hpp] [115] - 启动线程Thread-3 ... 成功[2024-11-29 10:51:04] [INFO] [400187] [ThreadPool.hpp] [115] - 启动线程Thread-4 ... 成功[2024-11-29 10:51:04] [INFO] [400187] [ThreadPool.hpp] [115] - 启动线程Thread-5 ... 成功[2024-11-29 10:51:04] [INFO] [400187] [ThreadPool.hpp] [42] - 线程: Thread-3, 进入了HandletTask的执行逻辑[2024-11-29 10:51:04] [DEBUG] [400187] [Task.hpp] [13] - 我是一个将数据推送到服务器的任务,正在被执行[Thread-3][2024-11-29 10:51:04] [INFO] [400187] [ThreadPool.hpp] [42] - 线程: Thread-2, 进入了HandletTask的执行逻辑[2024-11-29 10:51:04] [INFO] [400187] [ThreadPool.hpp] [42] - 线程: Thread-1, 进入了HandletTask的执行逻辑[2024-11-29 10:51:04] [INFO] [400187] [ThreadPool.hpp] [42] - 线程: Thread-5, 进入了HandletTask的执行逻辑[2024-11-29 10:51:04] [INFO] [400187] [ThreadPool.hpp] [42] - 线程: Thread-4, 进入了HandletTask的执行逻辑[2024-11-29 10:51:05] [DEBUG] [400187] [Task.hpp] [13] - 我是一个将数据推送到服务器的任务,正在被执行[Thread-3][2024-11-29 10:51:06] [DEBUG] [400187] [Task.hpp] [13] - 我是一个将数据推送到服务器的任务,正在被执行[Thread-2][2024-11-29 10:51:07] [DEBUG] [400187] [Task.hpp] [13] - 我是一个将数据推送到服务器的任务,正在被执行[Thread-1][2024-11-29 10:51:08] [DEBUG] [400187] [Task.hpp] [13] - 我是一个将数据推送到服务器的任务,正在被执行[Thread-5][2024-11-29 10:51:09] [DEBUG] [400187] [Task.hpp] [13] - 我是一个将数据推送到服务器的任务,正在被执行[Thread-4][2024-11-29 10:51:10] [DEBUG] [400187] [Task.hpp] [13] - 我是一个将数据推送到服务器的任务,正在被执行[Thread-3][2024-11-29 10:51:11] [DEBUG] [400187] [Task.hpp] [13] - 我是一个将数据推送到服务器的任务,正在被执行[Thread-2][2024-11-29 10:51:12] [DEBUG] [400187] [Task.hpp] [13] - 我是一个将数据推送到服务器的任务,正在被执行[Thread-1][2024-11-29 10:51:13] [DEBUG] [400187] [Task.hpp] [13] - 我是一个将数据推送到服务器的任务,正在被执行[Thread-5][2024-11-29 10:51:14] [INFO] [400187] [ThreadPool.hpp] [66] - 线程: Thread-2 退出[2024-11-29 10:51:14] [INFO] [400187] [ThreadPool.hpp] [66] - 线程: Thread-3 退出[2024-11-29 10:51:14] [INFO] [400187] [ThreadPool.hpp] [66] - 线程: Thread-1 退出[2024-11-29 10:51:14] [INFO] [400187] [ThreadPool.hpp] [66] - 线程: Thread-5 退出[2024-11-29 10:51:14] [INFO] [400187] [ThreadPool.hpp] [66] - 线程: Thread-4 退出[2024-11-29 10:51:17] [INFO] [400187] [ThreadPool.hpp] [124] - 回收线程Thread-1 ... 成功[2024-11-29 10:51:17] [INFO] [400187] [ThreadPool.hpp] [124] - 回收线程Thread-2 ... 成功[2024-11-29 10:51:17] [INFO] [400187] [ThreadPool.hpp] [124] - 回收线程Thread-3 ... 成功[2024-11-29 10:51:17] [INFO] [400187] [ThreadPool.hpp] [124] - 回收线程Thread-4 ... 成功[2024-11-29 10:51:17] [INFO] [400187] [ThreadPool.hpp] [124] - 回收线程Thread-5 ... 成功

二:🔥 共勉

以上就是我对 【Linux】线程池设计 + 策略模式 的理解,觉得这篇博客对你有帮助的,可以点赞收藏关注支持一波~😉
【Linux】线程池设计 + 策略模式_线程池策略