苏州市建设交易中心网站,龙岩网红街,小型网站设计及建设论文范本,个人网站可以做什么#x1f308; 个人主页#xff1a;Zfox_ #x1f525; 系列专栏#xff1a;Linux 目录 一#xff1a;#x1f525; 线程池 1-1 ⽇志与策略模式1-2 线程池设计1-3 线程安全的单例模式1-3-1 什么是单例模式1-3-2 单例模式的特点1-3-3 饿汉实现⽅式和懒汉实现⽅式1-3-4 饿汉… 个人主页Zfox_ 系列专栏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 iostream
#include cstdio
#include string
#include fstream
#include sstream
#include memory
#include filesystem // c17
#include unistd.h
#include time.h
#include Mutex.hppnamespace LogModule
{using namespace LockModule;// 获取一下当前系统的时间std::string CurrentTime(){time_t time_stamp ::time(nullptr);struct tm curr;localtime_r(time_stamp, curr); // 时间戳获取可读性较强的时间信息Schar buffer[1024];// bugsnprintf(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.txtstd::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_sharedConsoleLogStrategy();}void EnableConsoleLog(){_strategy std::make_sharedConsoleLogStrategy();}void EnableFileLog(){_strategy std::make_sharedFileLogStrategy();}~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();}templatetypename TLogMessage 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; // 进程pidstd::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_ptrLogStrategy _strategy; // 日志刷新的策略方案};Logger logger;#define LOG(Level) logger(Level, __FILE__, __LINE__)
#define ENABLE_CONSOLE_LOG() logger.EnableConsoleLog()
#define ENABLE_FILE_LOG() logger.EnableFileLog()
}使⽤样例:
#include Log.hppusing 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 线程池设计
线程池
线程池通过一个线程安全的阻塞任务队列加上一个或一个以上的线程实现线程池中的线程可以从阻塞队列中获取任务进行任务处理当线程都处于繁忙状态时可以将任务加入阻塞队列中等到其它的线程空闲后进行处理。可以避免大量线程频繁创建或销毁所带来的时间成本也可以避免在峰值压力下系统资源耗尽的风险并且可以统一对线程池中的线程进行管理调度监控。 线程池的应⽤场景 需要⼤量的线程来完成任务且完成任务的时间⽐较短。 ⽐如WEB服务器完成⽹⻚请求这样的任务使⽤线程池技术是⾮常合适的。因为单个任务⼩⽽任务数量巨⼤你可以想象⼀个热⻔⽹站的点击次数。 但对于⻓时间的任务⽐如⼀个Telnet连接请求线程池的优点就不明显了。因为Telnet会话时间⽐线程的创建时间⼤多了。 对性能要求苛刻的应⽤⽐如要求服务器迅速响应客⼾请求。 接受突发性的⼤量请求但不⾄于使服务器因此产⽣⼤量线程的应⽤。突发性⼤量客⼾请求在没有线程池情况下将产⽣⼤量线程虽然理论上⼤部分操作系统线程数⽬最⼤值不是问题短时间内产⽣⼤量线程可能使内存到达极限出现错误。
️ 线程池的种类
创建固定数量线程池循环从任务队列中获取任务对象获取到任务对象后执⾏任务对象中的任务接⼝浮动线程池其他同上此处我们选择固定线程个数的线程池。 ThreadPool.hpp
#pragma once#include iostream
#include string
#include queue
#include vector
#include memory
#include Log.hpp
#include Mutex.hpp
#include Cond.hpp
#include Thread.hppnamespace 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_ptrThread;const static int defaultnum 5;template typename Tclass 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_sharedThread(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_num0)_cond.NotifyAll();}}~ThreadPool(){}private:std::vectorthread_t _threads;int _num;int _wait_num;std::queueT _taskq; // 临界资源Mutex _lock;Cond _cond;bool _isrunning;};
}Task.hpp
#pragma#include iostream
#include functional
#include Log.hppusing namespace LogModule;using task_t std::functionvoid(std::string name);void Push(std::string name)
{LOG(LogLevel::DEBUG) 我是一个将数据推送到服务器的任务正在被执行 [ name ];
}main.cc
#include ThreadPool.hpp
#include Task.hpp
#include memoryusing namespace ThreadPoolModule;int main()
{ENABLE_FILE_LOG();std::unique_ptrThreadPooltask_t tp std::make_uniqueThreadPooltask_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 -stdc17 -lpthread // 需要使⽤C17运行结果
$ ./a.out
[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [62] - ThreadPool
Construct()
[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [70] - init thread
Thread-0 done
[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [70] - init thread
Thread-1 done
[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [70] - init thread
Thread-2 done
[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [70] - init thread
Thread-3 done
[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [70] - init thread
Thread-4 done
[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [79] - start thread
Thread-0done
[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [79] - start thread
Thread-1done
[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [28] - Thread-0 is
running...
[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [79] - start thread
Thread-2done
[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [79] - start thread
Thread-3done
[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [28] - Thread-3 is
running...
[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [28] - Thread-2 is
running...
[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [79] - start thread
Thread-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 a
task
this is a task
[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [28] - Thread-1 is
running...
[2024-08-04 15:09:29] [INFO] [206342] [ThreadPool.hpp] [28] - Thread-4 is
running...
[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 a
task
this 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;}
}注意事项:
加锁解锁的位置双重 if 判定, 避免不必要的锁竞争volatile关键字防⽌过度优化 (指令重排序和从寄存器中读取数据) (可见性和有序性)
1-4 单例式线程池
ThreadPool.hpp
#pragma once#include iostream
#include string
#include queue
#include vector
#include memory
#include Mutex.hpp
#include Log.hpp
#include Cond.hpp
#include Thread.hppnamespace 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_ptrThread;const static int defaultnum 5;template typename Tclass 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 ThreadPoolT ) delete;const ThreadPoolT operator(const ThreadPoolT ) delete;ThreadPool(int num defaultnum): _num(num), _wait_num(0), _isrunning(false){for (int i 0; i _num; i){_threads.push_back(std::make_sharedThread(std::bind(ThreadPool::HandlerTask, this, std::placeholders::_1)));LOG(LogLevel::DEBUG) 构建线程 _threads.back()-Name() 对象 ... 成功;}}public:static ThreadPoolT *getInstance(){if (instance nullptr){LockGuard lockguard(mutex);if (instance nullptr){LOG(LogLevel::INFO) 单例首次被执行需要加载对象...;instance new ThreadPoolT();}}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::vectorthread_t _threads;int _num;int _wait_num;std::queueT _taskq; // 临界资源Mutex _lock;Cond _cond;bool _isrunning;static ThreadPoolT *instance;static Mutex mutex; // 只用来保护单例};template typename TThreadPoolT *ThreadPoolT::instance nullptr;template typename TMutex ThreadPoolT::mutex;
}测试代码
#include ThreadPool.hpp
#include Task.hpp
#include memoryusing namespace ThreadPoolModule;int main()
{ENABLE_CONSOLE_LOG();ThreadPooltask_t::getInstance()-Start();int cnt 10;while(cnt){ThreadPooltask_t::getInstance()-Equeue(Push);cnt--;sleep(1);}ThreadPooltask_t::getInstance()-Stop();sleep(3);ThreadPooltask_t::getInstance()-Wait()return 0;
}运行结果
roothcss-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】线程池设计 策略模式 的理解觉得这篇博客对你有帮助的可以点赞收藏关注支持一波~